From 4d480798e89c6b2f4c698c0e119be849b4da4273 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Tue, 5 May 2026 17:21:16 +0100 Subject: [PATCH 1/2] Tactical commit: things in 'stack_ops' really didn't belong in ops; moving. --- Makefile | 8 +- docs/State-of-play.md | 142 ++++++++++++++++++++++++++ src/c/environment/environment.c | 15 +-- src/c/io/read.c | 4 +- src/c/memory/pso.c | 81 +++++++++------ src/c/ops/reverse.c | 6 +- src/c/ops/stack_ops.c | 80 --------------- src/c/ops/stack_ops.h | 35 ------- src/c/payloads/stack.c | 173 +++++++++++++++++++------------- src/c/payloads/stack.h | 20 ++++ 10 files changed, 333 insertions(+), 231 deletions(-) delete mode 100644 src/c/ops/stack_ops.c delete mode 100644 src/c/ops/stack_ops.h diff --git a/Makefile b/Makefile index b6853b9..8609dfc 100644 --- a/Makefile +++ b/Makefile @@ -51,8 +51,14 @@ clean: coredumps: ulimit -c unlimited -repl: +repl: Makefile $(TARGET) $(TARGET) -ps1000 2> tmp/psse.log +run: Makefile $(TARGET) + $(TARGET) -ps1000v1023 2> tmp/psse.log + +install: Makefile $(TARGET) + cp $(TARGET) ~/bin + -include $(DEPS) diff --git a/docs/State-of-play.md b/docs/State-of-play.md index 39f44ff..60b6e10 100644 --- a/docs/State-of-play.md +++ b/docs/State-of-play.md @@ -1,5 +1,147 @@ # State of Play +## 20260505 + +### The stack frame corruption(?) bug + +I have a weird bug in `read_symbol`, which at present I'm not understanding. + +Stack frames in `0.1.0` are [paged space objects](https://www.journeyman.cc/blog/posts-output/2026-03-23-Paged-space-objects/), like all other objects; specifically they are objects of size class 4, which is to say they have a payload size of fourteen words. The first eight arguments to the function being called (which in most cases will be all the arguments) are held directly in the frame. + +`read_symbol` expects its arguments to be as follows (I'm numbering from zero here, although I consider that perverse and confusing, because the substrate language is C which uses numbering from zero:) + +| Argument | Expected value | Expected type | +| -------- | --------------- | ------------------------------------ | +| 0 | input stream | input stream | +| 1 | read table | store (cons, hashtable or namespace) | +| 2 | first character | character object | + +`read_symbol` then reads characters sequentially from the stream until it encounters a white-space character; for each character it reads, it creates a symbol object representing that character, and conses that object onto the list of the characters it has read so far. So if the user has typed + +> xyz + +the internal representation is now a sequence + +```lisp +(z y x) +``` + +Obviously, this now has to be reversed. So `read_symbol` then calls `reverse`. But wait! Because we're still in the bootstrap layer, the version of `read_symbol` I'm talking about is written in C. So *at the time of writing* it actually calls a wrapper function called `c_reverse` which builds the Lisp stack frame for `reverse` and then calls `reverse` with that stack frame. There was an earlier version of `c_reverse` which failed to create a new stack frame, and which would account for the bug I'm seeing; but that version has been replaced and the current version does certainly create the new stack frame: + +```c +/** + * @brief reverse a sequence. + * + * A sequence is a list or a string-like-thing. A dotted pair is not a + * sequence. + * + * @param sequence a pointer to a sequence. + * @return a sequence like the `sequence` passed, but reversed; or `nil` if + * the argument was not a sequence. + */ +struct pso_pointer c_reverse( struct pso_pointer frame_pointer, + struct pso_pointer sequence ) { + + struct pso_pointer result = nil; + + if ( stackp( frame_pointer ) ) { + result = reverse( make_frame(1, frame_pointer, sequence) ); + } + + return result; +} +``` + +So, I can see in the debugger that the sequence created in `read_symbol` is passed to `c_reverse` as the sequence argument; I can see it is put into the new frame as the first (index 0) argument; the new frame is directly passed into reverse. Reverse expects the argument in its stack frame to look like this: + +| Argument | Expected value | Expected type | +| -------- | -------------- | ------------------------------------------ | +| 0 | sequence | sequence (cons, keyword, string or symbol) | + + Reverse throws an exception: + +```lisp + +``` + +D'oh! And, of course, in trying to explain the bug, I've found the bug. It wasn't what I thought it was, so I was looking in the wrong place. It was this: + +```diff + struct pso_pointer sequence = + fetch_arg( pointer_to_pso4( frame_pointer ), 0 ); +- for ( struct pso_pointer cursor = sequence; !c_nilp( sequence ); ++ for ( struct pso_pointer cursor = sequence; !c_nilp( cursor ); + cursor = c_cdr( cursor ) ) { + struct pso2 *object = pointer_to_object( cursor ); + switch ( get_tag_value( cursor ) ) { + +``` + +I was checking for `nil` on the sequence, which obviously didn't change, not on the cursor, which did. D'oh! + +### About debuggers + +I switched to Eclipse for this session, because Eclipse has really good, really easy to use, debugger integration. But I don't, as I said yesterday, much like Eclipse. It is too helpful; it gets in the way too much. + +Zed, Gram, Gnome Builder and VS Codium (discussed yesterday) all claim to have debugger integration, and I'm pretty sure the debugger used in all cases is the [GNU debugger, `gdb`](https://sourceware.org/gdb/) (edited: I'm wrong. Zed, and so presumably also Gram, use [`lldb`](https://lldb.llvm.org/)). `Gdb` is an excellent debugger with a truly atrocious user interface, but fortunately there's a large range of tools which wrap more or less good user interfaces around `gdb`, of which I use (and like) ['seer'](https://github.com/epasveer/seer). However it's *much* more productive to have your debugger integrated with your editor. + +I've tried this morning to get each of these to enter a useful debugging session. It has taken some work. Gnome Builder fails (for me) because although selecting `Run with Debugger` from the `run` menu does start both a `psse` session and a `gdb` session, and although terminating the `psse` session does show `[Inferior 1 (process 248474) exited normally]` on the GDB console, when I attempt to set a breakpoint (you don't seem to be able to set on in the GUI), I get the following: + +``` +> break src/c/ops/eval_apply.c:784 +Make breakpoint pending on future shared library load? (y or [n]) [answered N; input not from terminal] +> n +Cannot execute this command without a live selected thread. +``` + +So there is something alive there, and probably with a bit of struggle I could make it work. + +Zed and Gram are much the same, because Gram is a fork of Zed. Zed appears(?) to copy VS Codium's (and thus VS Code's) approach to interacting with `gdb`. VS Codium *appears*(?) to need some sort of JSON configuration in `launch.json`. I've tried this: + +```json +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "PSSE Debug (gdb Attach)", + "type": "cppdbg", + "request": "attach", + "program": "target/psse", + // "args": ["-p", "-s1000", "-v1023"], + "processId": "${command:pickProcess}", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} +``` + +It does not work, at least not in VS Codium. + +Zed's debugger [configuration documentation](https://zed.dev/docs/debugger) is better. Using it, I was able to compose this stanza: + +```json + { + "label": "PSSE Start debugger config", + "adapter": "CodeLLDB", + "request": "launch", + "program": "target/psse", + "cwd": "$ZED_WORKTREE_ROOT", + }, + +``` + +which successfully launches a debugger session. It's easy to set breakpoints in the editor windows; it's probably as easy to find your way around variables and stack frames as it is in Eclipse or Seer, once you get used to it (I haven't yet). I haven't yet worked out how to get it to automatically rebuild before running if it needs to do so, but I expect I shall. This is usable; but I shall need to get used to it. + ## 20260504 My monster, she builds! diff --git a/src/c/environment/environment.c b/src/c/environment/environment.c index 8ca6b42..69a88d6 100644 --- a/src/c/environment/environment.c +++ b/src/c/environment/environment.c @@ -36,7 +36,7 @@ bool environment_initialised = false; /** * @brief Initialise a minimal environment, so that Lisp can be bootstrapped. - * + * * @param node the index of the node we are initialising. * @return a proto-environment on success, else an exception. */ @@ -81,10 +81,11 @@ struct pso_pointer initialise_environment( uint32_t node ) { } } if ( !exceptionp( result ) ) { + frame_pointer = inc_ref( make_frame(0, nil)); result = lisp_bind( make_frame ( 3, frame_pointer, - c_string_to_lisp_symbol( frame_pointer, U"niU" ), nil, + c_string_to_lisp_symbol( frame_pointer, U"nil" ), nil, nil ) ); debug_print( U"Environment after binding `nil`: ", DEBUG_BOOTSTRAP, 0 ); @@ -101,14 +102,14 @@ struct pso_pointer initialise_environment( uint32_t node ) { debug_print( U"\nEnvironment initialised successfully.\n", DEBUG_BOOTSTRAP, 0 ); - } - initialise_privileged_keywords(frame_pointer); + initialise_privileged_keywords(frame_pointer); - result = initialise_function_bindings(push_local( - frame_pointer, make_frame_with_env(0, frame_pointer, result))); + result = inc_ref( initialise_function_bindings(push_local( + frame_pointer, make_frame_with_env(0, frame_pointer, result)))); - dec_ref(frame_pointer); + dec_ref(frame_pointer); + } return result; } diff --git a/src/c/io/read.c b/src/c/io/read.c index 5e64005..ff0f516 100644 --- a/src/c/io/read.c +++ b/src/c/io/read.c @@ -143,8 +143,8 @@ struct pso_pointer read_number( struct pso_pointer frame_pointer ) { ? 0 : pointer_to_object( character )->payload.character.character; URL_FILE *input = pointer_to_object( stream )->payload.stream.stream; - for ( ; iswdigit( c ); c = url_fgetwc( input ) ) { - value = ( value * base ) + ( ( int ) c - ( int ) L'0' ); + for ( ; iswdigit( c ) || c == L','; c = url_fgetwc( input ) ) { + if ( iswdigit( c ) ){value = ( value * base ) + ( ( int ) c - ( int ) L'0' );} } url_ungetwc( c, input ); diff --git a/src/c/memory/pso.c b/src/c/memory/pso.c index aff210b..e16fafb 100644 --- a/src/c/memory/pso.c +++ b/src/c/memory/pso.c @@ -60,6 +60,9 @@ void print_allocation_table( ) { } #endif +struct pso_pointer cheaty_allocate(struct pso_pointer frame_pointer, char *tag, + uint8_t size_class); + /** * @brief a means of creating a cons cell without using a stack frame, to * prevent runaway recursion. @@ -71,7 +74,7 @@ void print_allocation_table( ) { */ struct pso_pointer cheaty_make_cons( struct pso_pointer car, struct pso_pointer cdr ) { - struct pso_pointer result = allocate( nil, CONSTAG, 2 ); + struct pso_pointer result = cheaty_allocate( nil, CONSTAG, 2 ); struct pso2 *obj = pointer_to_object( result ); obj->payload.cons.car = car; @@ -80,6 +83,46 @@ struct pso_pointer cheaty_make_cons( struct pso_pointer car, return result; } +/** + * Special variant of allocate especially for cheaty_make_cons, so we don't + * get excessive spurius missing stack frame warnings. Not to be called + * outside this file! + */ +struct pso_pointer cheaty_allocate(struct pso_pointer frame_pointer, char *tag, + uint8_t size_class) { + struct pso_pointer result = pop_freelist( size_class ); +#ifdef DEBUG + debug_printf( DEBUG_ALLOC, 0, + L"\nAllocating object of size class %d with tag `%s`... ", + size_class, tag ); +#endif + + struct pso2 *obj = pointer_to_object( result ); + strncpy( ( char * ) ( obj->header.tag.bytes.mnemonic ), tag, TAGLENGTH ); + + debug_printf( DEBUG_ALLOC, 0, L"at page %d, offset %d... ", result.page, + result.offset ); + if ( stackp( frame_pointer ) ) { + struct pso4 *frame = pointer_to_pso4( frame_pointer ); + // You can't make a stack frame in the middle of making a stack + // frame. Infinite recursion. So we have to cheat. + struct pso_pointer locals = + cheaty_make_cons( result, frame->payload.stack_frame.locals ); + frame->payload.stack_frame.locals = locals; + } +#ifdef DEBUG + allocation_table[size_class][allocation_table_allocated]++; +#endif + +#ifdef DEBUG + debug_print( exceptionp( result ) ? L"fail\n" : L"success\n", DEBUG_ALLOC, + 0 ); +#endif + + return result; +} + + /** * @brief Allocate an object of this `size_class` with this `tag`. * @@ -100,42 +143,14 @@ struct pso_pointer cheaty_make_cons( struct pso_pointer car, */ struct pso_pointer allocate( struct pso_pointer frame_pointer, char *tag, uint8_t size_class ) { - struct pso_pointer result = pop_freelist( size_class ); - if ( memory_initialised && c_nilp( frame_pointer ) ) { fputws( L"\nWARNING: No stack frame passed to `allocate`.\n", stderr ); } -#ifdef DEBUG - debug_printf( DEBUG_ALLOC, 0, - L"\nAllocating object of size class %d with tag `%s`... ", - size_class, tag ); -#endif - struct pso2 *obj = pointer_to_object( result ); - strncpy( ( char * ) ( obj->header.tag.bytes.mnemonic ), tag, TAGLENGTH ); - - debug_printf( DEBUG_ALLOC, 0, L"at page %d, offset %d... ", result.page, - result.offset ); - if ( stackp( frame_pointer ) ) { - struct pso4 *frame = pointer_to_pso4( frame_pointer ); - // You can't make a stack frame in the middle of making a stack - // frame. Infinite recursion. So we have to cheat. - struct pso_pointer locals = - cheaty_make_cons( result, frame->payload.stack_frame.locals ); - frame->payload.stack_frame.locals = locals; - } -#ifdef DEBUG - allocation_table[size_class][allocation_table_allocated]++; -#endif - -#ifdef DEBUG - debug_print( exceptionp( result ) ? L"fail\n" : L"success\n", DEBUG_ALLOC, - 0 ); -#endif - - return result; + return cheaty_allocate(frame_pointer, tag, size_class); } + int payload_size( struct pso2 *object ) { // TODO: Unit tests DEFINITELY needed! int sc = object->header.tag.bytes.size_class; @@ -157,7 +172,7 @@ int payload_size( struct pso2 *object ) { */ struct pso_pointer inc_ref( struct pso_pointer pointer ) { if ( c_nilp( pointer ) || c_truep( pointer ) ) { - /* You can't do this and there's no point trying or cluttering the + /* You can't do this and there's no point trying or cluttering the logs. */ return pointer; } else if ( freep( pointer ) ) { @@ -204,7 +219,7 @@ struct pso_pointer inc_ref( struct pso_pointer pointer ) { */ struct pso_pointer dec_ref( struct pso_pointer pointer ) { if ( c_nilp( pointer ) || c_truep( pointer ) ) { - /* You can't do this and there's no point trying or cluttering the + /* You can't do this and there's no point trying or cluttering the logs. */ return pointer; } else if ( freep( pointer ) ) { diff --git a/src/c/ops/reverse.c b/src/c/ops/reverse.c index 296aaf3..c25a5b0 100644 --- a/src/c/ops/reverse.c +++ b/src/c/ops/reverse.c @@ -26,6 +26,7 @@ #include "ops/string_ops.h" #include "ops/truth.h" +#include "payloads/stack.h" /** * @brief reverse a sequence @@ -36,7 +37,7 @@ struct pso_pointer reverse( struct pso_pointer frame_pointer ) { struct pso_pointer result = nil; struct pso_pointer sequence = fetch_arg( pointer_to_pso4( frame_pointer ), 0 ); - for ( struct pso_pointer cursor = sequence; !c_nilp( sequence ); + for ( struct pso_pointer cursor = sequence; !c_nilp( cursor ); cursor = c_cdr( cursor ) ) { struct pso2 *object = pointer_to_object( cursor ); switch ( get_tag_value( cursor ) ) { @@ -104,7 +105,8 @@ struct pso_pointer c_reverse( struct pso_pointer frame_pointer, struct pso_pointer result = nil; if ( stackp( frame_pointer ) ) { - result = reverse( frame_pointer ); + result = reverse( make_frame(1, frame_pointer, sequence) ); } + return result; } diff --git a/src/c/ops/stack_ops.c b/src/c/ops/stack_ops.c deleted file mode 100644 index f1d14ea..0000000 --- a/src/c/ops/stack_ops.c +++ /dev/null @@ -1,80 +0,0 @@ -/** - * payloads/stack.c - * - * The execution stack. - * - * (c) 2026 Simon Brooke - * 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; -} diff --git a/src/c/ops/stack_ops.h b/src/c/ops/stack_ops.h deleted file mode 100644 index 059f61e..0000000 --- a/src/c/ops/stack_ops.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * ops/stack_ops.h - * - * Operations on a Lisp stack frame. - * - * (c) 2026 Simon Brooke - * Licensed under GPL version 2.0, or, at your option, any later version. - */ - -#ifndef __psse_ops_stack_ops_h -#define __psse_ops_stack_ops_h - -#include "memory/pointer.h" -#include "memory/pso4.h" - -/* - * number of arguments stored in a stack frame - */ -#define args_in_frame 8 - -/** - * @brief The maximum depth of stack before we throw an exception. - * - * `0` is interpeted as `unlimited`. - */ -extern uint32_t stack_limit; - -struct pso_pointer fetch_arg( struct pso4 *frame, unsigned int index ); - -struct pso_pointer fetch_env( struct pso_pointer frame_pointer ); - -struct pso_pointer push_local( struct pso_pointer frame_pointer, - struct pso_pointer local ); - -#endif diff --git a/src/c/payloads/stack.c b/src/c/payloads/stack.c index 75472f5..978f356 100644 --- a/src/c/payloads/stack.c +++ b/src/c/payloads/stack.c @@ -26,6 +26,69 @@ #include "ops/list_ops.h" #include "ops/stack_ops.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; +} + /** * @brief Add an argument to this (already initialised) stack frame, updating * the args count. @@ -60,22 +123,11 @@ struct pso_pointer add_arg( struct pso_pointer frame_pointer, struct pso_pointer } /** - * @brief Construct a stack frame with this `previous` pointer, and arguments - * taken from the remaining arguments to this function, which should all be - * struct pso_pointer. - * - * @param arg_count the count of arguments to the Lisp function. - * @param previous the parent stack frame. - * @param ... the arguments to the Lisp function, all of which must be of type - * `struct pso_pointer`. - * @return struct pso_pointer a pointer to a populated stack frame which may be - * passed to the Lisp function. + * @brief internal shared guts of make_frame variants. **Does not** set up the + * `env` pointer of the new frame -- callers are responsible for doing so. */ -struct pso_pointer make_frame( int arg_count, struct pso_pointer previous, - ... ) { - va_list args; - va_start( args, previous ); - +struct pso_pointer in_make_frame( int arg_count, struct pso_pointer previous, + va_list args ) { /* NOTE! It is really important not to `push_local` the new_pointer here, * since that would stop stack frames and all the temporary objects they * curate ever being garbage collected! */ @@ -94,13 +146,13 @@ struct pso_pointer make_frame( int arg_count, struct pso_pointer previous, struct pso4 *prev_frame = pointer_to_pso4( previous ); new_frame->payload.stack_frame.depth = prev_frame->payload.stack_frame.depth + 1; - new_frame->payload.stack_frame.env = - prev_frame->payload.stack_frame.env; + new_frame->payload.stack_frame.previous = inc_ref( previous ); } else { new_frame->payload.stack_frame.depth = 0; + new_frame->payload.stack_frame.previous = nil; } - new_frame->payload.stack_frame.previous = inc_ref( previous ); + new_frame->payload.stack_frame.env = nil; debug_printf( DEBUG_ALLOC, 1, L"depth is %d...\n", new_frame->payload.stack_frame.depth ); @@ -136,6 +188,34 @@ struct pso_pointer make_frame( int arg_count, struct pso_pointer previous, return new_pointer; } +/** + * @brief Construct a stack frame with this `previous` pointer, and arguments + * taken from the remaining arguments to this function, which should all be + * struct pso_pointer. + * + * @param arg_count the count of arguments to the Lisp function. + * @param previous the parent stack frame. + * @param ... the arguments to the Lisp function, all of which must be of type + * `struct pso_pointer`. + * @return struct pso_pointer a pointer to a populated stack frame which may be + * passed to the Lisp function. + */ +struct pso_pointer make_frame( int arg_count, struct pso_pointer previous, + ... ) { + va_list args; + va_start( args, previous ); + + struct pso_pointer new_pointer = in_make_frame( arg_count, previous, args); + struct pso4* new_frame = pointer_to_pso4(new_pointer); + + new_frame->payload.stack_frame.env = stackp(previous) ? + inc_ref(pointer_to_pso4(previous)->payload.stack_frame.env) : nil; + + va_end(args); + + return new_pointer; +} + /** * @brief variant of make_frame with an explicit replacement environment, to * be called by functions like `binding` which add bindings to their upstack @@ -158,60 +238,10 @@ struct pso_pointer make_frame_with_env( int arg_count, va_list args; va_start( args, env ); - struct pso4 *prev_frame = pointer_to_pso4( previous ); - /* NOTE! It is really important not to `push_local` the new_pointer here, - * since that would stop stack frames and all the temporary objects they - * curate ever being garbage collected! */ - struct pso_pointer new_pointer = allocate( previous, STACKTAG, 4 ); - struct pso4 *new_frame = pointer_to_pso4( new_pointer ); + struct pso_pointer new_pointer = in_make_frame( arg_count, previous, args); + pointer_to_pso4(new_pointer)->payload.stack_frame.env = inc_ref( env); -#ifdef DEBUG - debug_printf( DEBUG_ALLOC, 0, - L"\nAllocating stack frame with %d arguments at page %d, " - L"offset %d...\n", - arg_count, new_pointer.page, new_pointer.offset ); -#endif - - prev_frame->payload.stack_frame.previous = inc_ref( previous ); - - if ( stackp( previous ) ) { - new_frame->payload.stack_frame.depth = - prev_frame->payload.stack_frame.depth + 1; - } else { - new_frame->payload.stack_frame.depth = 0; - } - - debug_printf( DEBUG_ALLOC, 1, L"depth is %d...\n", - new_frame->payload.stack_frame.depth ); - - int cursor = 0; - new_frame->payload.stack_frame.args = arg_count; - new_frame->payload.stack_frame.env = env; - - for ( ; cursor < arg_count && cursor < args_in_frame; cursor++ ) { - struct pso_pointer argument = va_arg( args, struct pso_pointer ); - - new_frame->payload.stack_frame.arg[cursor] = inc_ref( argument ); - } - if ( cursor < arg_count ) { - struct pso_pointer more_args = nil; - - for ( ; cursor < arg_count; cursor++ ) { - more_args = - make_cons( previous, va_arg( args, struct pso_pointer ), - more_args ); - } - - new_frame->payload.stack_frame.more = c_reverse( previous, more_args ); - } else { - for ( ; cursor < args_in_frame; cursor++ ) { - new_frame->payload.stack_frame.arg[cursor] = nil; - } - } - - debug_printf( DEBUG_ALLOC, 1, - L"Allocation of stack frame at page %d, offset %d completed.\n", - new_pointer.page, new_pointer.offset ); + va_end(args); return new_pointer; } @@ -258,6 +288,7 @@ struct pso_pointer make_frame_with_arglist_and_env( struct pso_pointer inc_ref( prev_frame->payload.stack_frame.env ); } else { new_frame->payload.stack_frame.depth = 0; + new_frame->payload.stack_frame.env = nil; } debug_printf( DEBUG_ALLOC, 1, L"depth is %d...\n", diff --git a/src/c/payloads/stack.h b/src/c/payloads/stack.h index d89d705..62f9a7b 100644 --- a/src/c/payloads/stack.h +++ b/src/c/payloads/stack.h @@ -41,6 +41,26 @@ struct stack_frame_payload { uint32_t depth; }; +/* + * number of arguments stored in a stack frame + */ +#define args_in_frame 8 + +/** + * @brief The maximum depth of stack before we throw an exception. + * + * `0` is interpeted as `unlimited`. + */ +extern uint32_t stack_limit; + +struct pso_pointer fetch_arg( struct pso4 *frame, unsigned int index ); + +struct pso_pointer fetch_env( struct pso_pointer frame_pointer ); + +struct pso_pointer push_local( struct pso_pointer frame_pointer, + struct pso_pointer local ); + + struct pso_pointer make_frame( int arg_count, struct pso_pointer previous, ... ); From 818293d4f146446ec89c8e29e9aee86189296c83 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Tue, 5 May 2026 19:16:44 +0100 Subject: [PATCH 2/2] Moved everything from ops/stack_ops (which were not ops) to payloads/stack. Added io functions to function_bindings. --- Makefile | 4 +- src/c/environment/environment.c | 2 +- src/c/environment/function_bindings.c | 56 ++++++++++++++++++++++++++- src/c/io/io.c | 24 ++++-------- src/c/io/io.h | 37 +++++++----------- src/c/io/peek.c | 42 ++++++++++++++++++++ src/c/io/peek.h | 20 ++++++++++ src/c/io/print.c | 4 +- src/c/io/read.c | 2 +- src/c/memory/memory.c | 3 +- src/c/memory/node.c | 2 +- src/c/memory/pso4.h | 2 +- src/c/ops/assoc.c | 2 +- src/c/ops/bind.c | 2 +- src/c/ops/cond.c | 2 +- src/c/ops/dump.c | 2 +- src/c/ops/eq.c | 2 +- src/c/ops/eval_apply.c | 2 +- src/c/ops/inspect.c | 2 +- src/c/ops/list_ops.c | 2 +- src/c/ops/mapcar.c | 2 +- src/c/ops/progn.c | 2 +- src/c/ops/quote.c | 2 +- src/c/ops/repl.c | 2 +- src/c/ops/reverse.c | 2 +- src/c/ops/truth.c | 2 +- src/c/payloads/cons.c | 2 +- src/c/payloads/exception.c | 2 +- src/c/payloads/psse_string.c | 2 +- src/c/payloads/read_stream.c | 3 ++ src/c/payloads/stack.c | 2 +- src/c/payloads/stack.h | 28 +------------- src/c/payloads/stack_payload.h | 45 +++++++++++++++++++++ src/c/psse.c | 1 - 34 files changed, 217 insertions(+), 94 deletions(-) create mode 100644 src/c/io/peek.c create mode 100644 src/c/io/peek.h create mode 100644 src/c/payloads/stack_payload.h diff --git a/Makefile b/Makefile index 8609dfc..97bbf76 100644 --- a/Makefile +++ b/Makefile @@ -52,10 +52,10 @@ coredumps: ulimit -c unlimited repl: Makefile $(TARGET) - $(TARGET) -ps1000 2> tmp/psse.log + $(TARGET) -p -s1000 -v1023 2> tmp/psse.log run: Makefile $(TARGET) - $(TARGET) -ps1000v1023 2> tmp/psse.log + $(TARGET) -p -s1000 -v1023 2> tmp/psse.log install: Makefile $(TARGET) cp $(TARGET) ~/bin diff --git a/src/c/environment/environment.c b/src/c/environment/environment.c index 69a88d6..27c9fa5 100644 --- a/src/c/environment/environment.c +++ b/src/c/environment/environment.c @@ -25,7 +25,7 @@ #include "payloads/psse_string.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/truth.h" #include "payloads/stack.h" diff --git a/src/c/environment/function_bindings.c b/src/c/environment/function_bindings.c index 8b039d1..50225a9 100644 --- a/src/c/environment/function_bindings.c +++ b/src/c/environment/function_bindings.c @@ -18,6 +18,12 @@ #include "debug.h" #include "environment/privileged_keywords.h" + +#include "io/io.h" +#include "io/peek.h" +#include "io/print.h" +#include "io/read.h" + #include "memory/node.h" #include "memory/pointer.h" #include "memory/tags.h" @@ -35,7 +41,7 @@ #include "ops/quote.h" #include "ops/repl.h" #include "ops/reverse.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/string_ops.h" #include "ops/truth.h" @@ -133,6 +139,54 @@ struct function_data { /** initialisers for functions */ struct function_data function_initialisers[] = { +#ifdef _psse_io_io_h + {U"close", U"(close stream): close `stream`.", &lisp_close}, + {U"open", + U"(open stream), (open stream write?): open `stream`; if `write?` is " + U"present and is non-nil, open for writing, else for reading.", + &lisp_open}, + {U"slurp", + U"(slurp stream): read the whole contents of this `stream`, " + U"which may " + U"be an open stream open for reading or a URL, into a string, and return " + U"the " + U"string.", + &lisp_slurp}, +#endif +#ifdef __psse_io_peek_h + {U"peek", + U"(peek stream): return the next character which may be read from " + U"`stream`, without removing it.", + &peek}, +#endif +#ifdef __psse_io_print_h + {U"print", + U"(print object), (print object stream) print this `object` in a format " + U"suitable to be read by `read`, q.v.; if `stream` is specified and is a " + U"stream open for writing, to that stream.", + &print}, + {U"princ", + U"(princ object), (princ object stream) print this `object` in a format " + U"more suited to human readers; if `stream` is specified and is a stream " + U"open for writing, to that stream.", + &print}, +#endif +#ifdef __psse_io_read_h + {U"read", + U"(read stream) read one complete Lisp expression from `stream`, and " + U"return that expression unevaluated.", + &read}, + {U"read-character", + U"(read_character stream): read a single character from `stream` and " + U"return it.", + &read_character}, + {U"read_number", + U"(read-number stream): read a number from `stream` and return it.", + &read_number}, + {U"read_symbol", + U"(read-symbol stream): read a symbol from `stream` and return it.", + &read_symbol}, +#endif #ifdef __psse_ops_assoc_h {U"assoc", U"(assoc key store): search `store` for the value associated with " diff --git a/src/c/io/io.c b/src/c/io/io.c index 9a95c2f..4636bc3 100644 --- a/src/c/io/io.c +++ b/src/c/io/io.c @@ -47,7 +47,7 @@ #include "ops/assoc.h" #include "ops/bind.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/string_ops.h" #include "ops/truth.h" @@ -393,8 +393,7 @@ struct pso_pointer push_back_character( struct pso_pointer c, * @param env my environment. * @return T if the stream was successfully closed, else nil. */ -struct pso_pointer lisp_close( struct pso_pointer frame_pointer, - struct pso_pointer env ) { +struct pso_pointer lisp_close( struct pso_pointer frame_pointer) { struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer result = nil; if ( readp( fetch_arg( frame, 0 ) ) || writep( fetch_arg( frame, 0 ) ) ) { @@ -591,14 +590,10 @@ URL_FILE *stream_get_url_file( struct pso_pointer s ) { * * * (open url) * - * @param frame my stack frame. * @param frame_pointer a pointer to my stack frame. - * @param env my environment. - * @return a string of one character, namely the next available character - * on my stream, if any, else nil. + * @return a stream open on the URL indicated by the first argument. */ -struct pso_pointer lisp_open( struct pso_pointer frame_pointer, - struct pso_pointer env ) { +struct pso_pointer lisp_open( struct pso_pointer frame_pointer) { struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer result = nil; if ( stringp( fetch_arg( frame, 0 ) ) ) { @@ -650,18 +645,13 @@ struct pso_pointer lisp_open( struct pso_pointer frame_pointer, * Function: return a string representing all characters from the stream * indicated by arg 0; further arguments are ignored. * - * TODO: it should be possible to optionally pass a string URL to this function, - * * * (slurp stream) * - * @param frame my stack frame. * @param frame_pointer a pointer to my stack frame. - * @param env my environment. - * @return a string of one character, namely the next available character - * on my stream, if any, else nil. + * @return return a string representing all characters from the stream + * indicated by arg 0 */ -struct pso_pointer lisp_slurp( struct pso_pointer frame_pointer, - struct pso_pointer env ) { +struct pso_pointer lisp_slurp( struct pso_pointer frame_pointer) { struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer result = nil; if ( readp( fetch_arg( frame, 0 ) ) ) { diff --git a/src/c/io/io.h b/src/c/io/io.h index baf9e52..cd37d5d 100644 --- a/src/c/io/io.h +++ b/src/c/io/io.h @@ -11,8 +11,8 @@ #ifndef __psse_io_io_h #define __psse_io_io_h -#include #include +#include /* * wide characters @@ -24,12 +24,11 @@ extern CURLSH *io_share; -int initialise_io( ); -struct pso_pointer initialise_default_streams( struct pso_pointer - frame_pointer, - struct pso_pointer env ); +int initialise_io(); +struct pso_pointer initialise_default_streams(struct pso_pointer frame_pointer, + struct pso_pointer env); -#define C_IO_IN L"*in*" +#define C_IO_IN L"*in*" #define C_IO_OUT L"*out*" #define C_IO_LOG L"*log*" #define C_IO_READBASE L"*read_base*" @@ -50,25 +49,19 @@ extern struct pso_pointer lisp_stderr; extern struct pso_pointer lisp_io_prompt; +URL_FILE *file_to_url_file(FILE *f); +wint_t url_fgetwc(URL_FILE *input); +wint_t url_ungetwc(wint_t wc, URL_FILE *input); +struct pso_pointer push_back_character(struct pso_pointer c, + struct pso_pointer r); -URL_FILE *file_to_url_file( FILE * f ); -wint_t url_fgetwc( URL_FILE * input ); -wint_t url_ungetwc( wint_t wc, URL_FILE * input ); +struct pso_pointer get_default_stream(bool inputp, struct pso_pointer env); +URL_FILE *stream_get_url_file(struct pso_pointer s); -struct pso_pointer push_back_character( struct pso_pointer c, - struct pso_pointer r ); - -struct pso_pointer get_default_stream( bool inputp, struct pso_pointer env ); - -URL_FILE *stream_get_url_file( struct pso_pointer s ); - -struct pso_pointer -lisp_close( struct pso_pointer frame_pointer, struct pso_pointer env ); -struct pso_pointer -lisp_open( struct pso_pointer frame_pointer, struct pso_pointer env ); -struct pso_pointer -lisp_slurp( struct pso_pointer frame_pointer, struct pso_pointer env ); +struct pso_pointer lisp_close(struct pso_pointer frame_pointer); +struct pso_pointer lisp_open(struct pso_pointer frame_pointer); +struct pso_pointer lisp_slurp(struct pso_pointer frame_pointer); #endif diff --git a/src/c/io/peek.c b/src/c/io/peek.c new file mode 100644 index 0000000..b926456 --- /dev/null +++ b/src/c/io/peek.c @@ -0,0 +1,42 @@ +/** + * io/peek.c + * + * Post Scarcity Software Environment: peek. + * + * look at the next character on the input stream, without consuming it. + * + * (c) 2026 Simon Brooke + * Licensed under GPL version 2.0, or, at your option, any later version. + */ + +#include + +#include "io/fopen.h" +#include "io/io.h" + +#include "memory/node.h" +#include "memory/pointer.h" +#include "memory/pso2.h" + +#include "payloads/character.h" + +/** + * @brief look at the next character on the input stream, without consuming it. + * + * (peek stream) + */ +struct pso_pointer peek(struct pso_pointer frame_pointer) { + struct pso_pointer result = nil; + struct pso_pointer input = + pointer_to_pso4(frame_pointer)->payload.stack_frame.arg[0]; + + if (readp(input)) { + URL_FILE *stream = pointer_to_object(input)->payload.stream.stream; + wint_t c = url_fgetwc(stream); + url_ungetwc(c, stream); + + result = make_character(frame_pointer, c); + } + return result; +} + diff --git a/src/c/io/peek.h b/src/c/io/peek.h new file mode 100644 index 0000000..06b6b3f --- /dev/null +++ b/src/c/io/peek.h @@ -0,0 +1,20 @@ +/** + * io/peek.c + * + * Post Scarcity Software Environment: peek. + * + * peek basic Lisp objects..This is :bootstrap layer peek; it needs to be + * able to peek characters, symbols, integers, lists and dotted pairs. I + * don't think it needs to be able to peek anything else. + * + * (c) 2026 Simon Brooke + * Licensed under GPL version 2.0, or, at your option, any later version. + */ + +#ifndef __psse_io_peek_h +#define __psse_io_peek_h +#include + +struct pso_pointer peek( struct pso_pointer frame_pointer ); + +#endif diff --git a/src/c/io/print.c b/src/c/io/print.c index d1dfcb4..a850e72 100644 --- a/src/c/io/print.c +++ b/src/c/io/print.c @@ -44,7 +44,7 @@ #include "payloads/exception.h" #include "payloads/integer.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/truth.h" struct pso_pointer in_write( struct pso_pointer p, URL_FILE * output, @@ -295,6 +295,8 @@ struct pso_pointer c_write(struct pso_pointer frame_pointer, /** * @brief Simple print for bootstrap layer. * + * (print object stream) + * * @param p pointer to the object to print. * @param stream if a pointer to an open write stream, print to there. * @return struct pso_pointer `nil`, or an exception if some erroe occurred. diff --git a/src/c/io/read.c b/src/c/io/read.c index ff0f516..5c09457 100644 --- a/src/c/io/read.c +++ b/src/c/io/read.c @@ -40,7 +40,7 @@ #include "ops/assoc.h" #include "ops/reverse.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/string_ops.h" #include "ops/truth.h" diff --git a/src/c/memory/memory.c b/src/c/memory/memory.c index bc1e722..efb9f81 100644 --- a/src/c/memory/memory.c +++ b/src/c/memory/memory.c @@ -23,11 +23,12 @@ #include "memory/pso2.h" #include "memory/tags.h" -#include "ops/truth.h" #include "payloads/exception.h" +#include "payloads/stack.h" #include "ops/bind.h" #include "ops/string_ops.h" +#include "ops/truth.h" /** * @brief Freelists for each size class. diff --git a/src/c/memory/node.c b/src/c/memory/node.c index 42ff995..015164d 100644 --- a/src/c/memory/node.c +++ b/src/c/memory/node.c @@ -22,7 +22,7 @@ #include "payloads/exception.h" #include "ops/eq.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/string_ops.h" #include "ops/truth.h" diff --git a/src/c/memory/pso4.h b/src/c/memory/pso4.h index 59996f7..cae50b2 100644 --- a/src/c/memory/pso4.h +++ b/src/c/memory/pso4.h @@ -15,7 +15,7 @@ #include "memory/header.h" #include "payloads/free.h" -#include "payloads/stack.h" +#include "payloads/stack_payload.h" /** * @brief A paged space object of size class 4, 16 words total, 14 words diff --git a/src/c/ops/assoc.c b/src/c/ops/assoc.c index 647b7bf..401aeb1 100644 --- a/src/c/ops/assoc.c +++ b/src/c/ops/assoc.c @@ -21,7 +21,7 @@ #include "payloads/stack.h" #include "ops/eq.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/truth.h" /** diff --git a/src/c/ops/bind.c b/src/c/ops/bind.c index 743de6b..fbcbfe5 100644 --- a/src/c/ops/bind.c +++ b/src/c/ops/bind.c @@ -16,7 +16,7 @@ #include "memory/pso4.h" #include "memory/tags.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "payloads/cons.h" #include "payloads/function.h" diff --git a/src/c/ops/cond.c b/src/c/ops/cond.c index f2949d4..f22a20f 100644 --- a/src/c/ops/cond.c +++ b/src/c/ops/cond.c @@ -13,7 +13,7 @@ #include "ops/eval_apply.h" #include "ops/progn.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/string_ops.h" #include "ops/truth.h" diff --git a/src/c/ops/dump.c b/src/c/ops/dump.c index f50cc14..c39b871 100644 --- a/src/c/ops/dump.c +++ b/src/c/ops/dump.c @@ -23,7 +23,7 @@ #include "memory/tags.h" #include "io/print.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "payloads/lambda.h" diff --git a/src/c/ops/eq.c b/src/c/ops/eq.c index d7b4f38..17e0f11 100644 --- a/src/c/ops/eq.c +++ b/src/c/ops/eq.c @@ -19,7 +19,7 @@ #include "payloads/function.h" #include "payloads/integer.h" #include "payloads/stack.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/truth.h" /** diff --git a/src/c/ops/eval_apply.c b/src/c/ops/eval_apply.c index 361c911..03c1411 100644 --- a/src/c/ops/eval_apply.c +++ b/src/c/ops/eval_apply.c @@ -33,7 +33,7 @@ #include "ops/eval_apply.h" #include "ops/progn.h" #include "ops/reverse.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/string_ops.h" #include "ops/truth.h" diff --git a/src/c/ops/inspect.c b/src/c/ops/inspect.c index 9d3ce60..67c883d 100644 --- a/src/c/ops/inspect.c +++ b/src/c/ops/inspect.c @@ -18,7 +18,7 @@ #include "memory/pso4.h" #include "memory/tags.h" #include "ops/inspect.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" /** * Function: dump/ diff --git a/src/c/ops/list_ops.c b/src/c/ops/list_ops.c index 6ef05b9..5cb3151 100644 --- a/src/c/ops/list_ops.c +++ b/src/c/ops/list_ops.c @@ -12,7 +12,7 @@ #include "memory/pso2.h" #include "memory/pso4.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "payloads/cons.h" #include "payloads/integer.h" diff --git a/src/c/ops/mapcar.c b/src/c/ops/mapcar.c index 5f74aae..e09379d 100644 --- a/src/c/ops/mapcar.c +++ b/src/c/ops/mapcar.c @@ -19,7 +19,7 @@ #include "ops/eval_apply.h" #include "ops/reverse.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/truth.h" #include "payloads/cons.h" diff --git a/src/c/ops/progn.c b/src/c/ops/progn.c index ac3f722..3fdef99 100644 --- a/src/c/ops/progn.c +++ b/src/c/ops/progn.c @@ -17,7 +17,7 @@ #include "memory/tags.h" #include "ops/eval_apply.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "payloads/cons.h" #include "payloads/stack.h" diff --git a/src/c/ops/quote.c b/src/c/ops/quote.c index 88ec694..f1d3595 100644 --- a/src/c/ops/quote.c +++ b/src/c/ops/quote.c @@ -12,7 +12,7 @@ #include "memory/pointer.h" #include "memory/pso4.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" /** * @brief Special form: protect an expression from evaluation. diff --git a/src/c/ops/repl.c b/src/c/ops/repl.c index a949b25..8d04f09 100644 --- a/src/c/ops/repl.c +++ b/src/c/ops/repl.c @@ -33,7 +33,7 @@ #include "ops/assoc.h" #include "ops/eval_apply.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/truth.h" /** diff --git a/src/c/ops/reverse.c b/src/c/ops/reverse.c index c25a5b0..a9be24f 100644 --- a/src/c/ops/reverse.c +++ b/src/c/ops/reverse.c @@ -19,7 +19,7 @@ #include "memory/pso4.h" #include "memory/tags.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "payloads/cons.h" #include "payloads/exception.h" #include "payloads/psse_string.h" diff --git a/src/c/ops/truth.c b/src/c/ops/truth.c index 4b7b9d8..9ac4ef0 100644 --- a/src/c/ops/truth.c +++ b/src/c/ops/truth.c @@ -14,7 +14,7 @@ #include "memory/node.h" #include "memory/pointer.h" #include "memory/pso4.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" /** * @brief true if `p` points to `nil`, else false. diff --git a/src/c/payloads/cons.c b/src/c/payloads/cons.c index dccdf13..cfca981 100644 --- a/src/c/payloads/cons.c +++ b/src/c/payloads/cons.c @@ -19,7 +19,7 @@ #include "payloads/cons.h" #include "payloads/exception.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/string_ops.h" /** diff --git a/src/c/payloads/exception.c b/src/c/payloads/exception.c index 7f40fc5..08dbfef 100644 --- a/src/c/payloads/exception.c +++ b/src/c/payloads/exception.c @@ -26,7 +26,7 @@ #include "payloads/exception.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" #include "ops/truth.h" #include "payloads/cons.h" #include diff --git a/src/c/payloads/psse_string.c b/src/c/payloads/psse_string.c index cc5eaef..a7d8dbe 100644 --- a/src/c/payloads/psse_string.c +++ b/src/c/payloads/psse_string.c @@ -23,7 +23,7 @@ #include "ops/string_ops.h" #include "payloads/cons.h" -#include "ops/stack_ops.h" +#include "payloads/stack.h" /** diff --git a/src/c/payloads/read_stream.c b/src/c/payloads/read_stream.c index 9cdce09..2058473 100644 --- a/src/c/payloads/read_stream.c +++ b/src/c/payloads/read_stream.c @@ -12,6 +12,9 @@ #include #include "io/fopen.h" +#include "io/io.h" + +#include "memory/node.h" #include "memory/pointer.h" #include "memory/pso.h" #include "memory/pso2.h" diff --git a/src/c/payloads/stack.c b/src/c/payloads/stack.c index 978f356..2e299a4 100644 --- a/src/c/payloads/stack.c +++ b/src/c/payloads/stack.c @@ -21,10 +21,10 @@ #include "memory/tags.h" #include "payloads/cons.h" +#include "payloads/stack.h" #include "ops/reverse.h" #include "ops/list_ops.h" -#include "ops/stack_ops.h" /** * @brief The maximum depth of stack before we throw an exception. diff --git a/src/c/payloads/stack.h b/src/c/payloads/stack.h index 62f9a7b..7c20409 100644 --- a/src/c/payloads/stack.h +++ b/src/c/payloads/stack.h @@ -13,33 +13,7 @@ #define __psse_payloads_stack_h #include "memory/pointer.h" - -/* - * number of arguments stored in a stack frame - */ -#define args_in_frame 8 - -/** - * A stack frame. - */ -struct stack_frame_payload { - /** the previous frame. */ - struct pso_pointer previous; - /** first 8 arument bindings. */ - struct pso_pointer arg[args_in_frame]; - /** list of any further argument bindings. */ - struct pso_pointer more; - /** the function to be called. */ - struct pso_pointer function; - /** the execute-time environment */ - struct pso_pointer env; - /** a list of objects created in the context of this frame */ - struct pso_pointer locals; - /** the number of arguments provided. */ - uint32_t args; - /** the depth of the stack below this frame */ - uint32_t depth; -}; +#include "payloads/stack_payload.h" /* * number of arguments stored in a stack frame diff --git a/src/c/payloads/stack_payload.h b/src/c/payloads/stack_payload.h new file mode 100644 index 0000000..95a9c2a --- /dev/null +++ b/src/c/payloads/stack_payload.h @@ -0,0 +1,45 @@ +/** + * payloads/stack_payload.h + * + * payload struct itself separated out from functions which interrogate it + * to avoid circularity with pso4. + * + * Sits in a pso4. + * + * (c) 2026 Simon Brooke + * Licensed under GPL version 2.0, or, at your option, any later version. + */ + +#ifndef __psse_payloads_stack_payload_h +#define __psse_payloads_stack_payload_h + +#include "memory/pointer.h" + +/* + * number of arguments stored in a stack frame + */ +#define args_in_frame 8 + +/** + * A stack frame. + */ +struct stack_frame_payload { + /** the previous frame. */ + struct pso_pointer previous; + /** first 8 arument bindings. */ + struct pso_pointer arg[args_in_frame]; + /** list of any further argument bindings. */ + struct pso_pointer more; + /** the function to be called. */ + struct pso_pointer function; + /** the execute-time environment */ + struct pso_pointer env; + /** a list of objects created in the context of this frame */ + struct pso_pointer locals; + /** the number of arguments provided. */ + uint32_t args; + /** the depth of the stack below this frame */ + uint32_t depth; +}; + +#endif \ No newline at end of file diff --git a/src/c/psse.c b/src/c/psse.c index bf7c745..0208c23 100644 --- a/src/c/psse.c +++ b/src/c/psse.c @@ -27,7 +27,6 @@ #include "memory/tags.h" #include "ops/repl.h" -#include "ops/stack_ops.h" #include "ops/string_ops.h" #include "ops/truth.h"