From efa6a3246d75abeca7af719b4bf2162cb11b577f Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Mon, 4 May 2026 16:15:57 +0100 Subject: [PATCH] Started work on binding functions. Not yet complete. --- docs/State-of-play.md | 30 +++- src/c/environment/function_bindings.c | 175 ++++++++++++++++++++++++ src/c/environment/function_bindings.h | 0 src/c/environment/privileged_keywords.c | 11 +- src/c/environment/privileged_keywords.h | 8 +- src/c/ops/assoc.c | 5 - src/c/ops/assoc.h | 2 + src/c/ops/cond.c | 31 ++--- src/c/ops/cond.h | 20 +++ src/c/ops/eq.c | 2 +- src/c/ops/eval_apply.c | 9 +- src/c/ops/inspect.c | 5 +- src/c/ops/inspect.h | 4 +- src/c/payloads/function.c | 25 ++++ src/c/payloads/function.h | 4 + src/c/payloads/special.c | 25 ++++ src/c/payloads/special.h | 6 +- 17 files changed, 321 insertions(+), 41 deletions(-) create mode 100644 src/c/environment/function_bindings.c create mode 100644 src/c/environment/function_bindings.h create mode 100644 src/c/ops/cond.h create mode 100644 src/c/payloads/function.c create mode 100644 src/c/payloads/special.c diff --git a/docs/State-of-play.md b/docs/State-of-play.md index c7c9832..39f44ff 100644 --- a/docs/State-of-play.md +++ b/docs/State-of-play.md @@ -24,7 +24,35 @@ The answer, dear reader, is that Emacs is determined to get in my way in every p #### Eclipse -When I finally switched away from using Emacs for everything +When I finally switched away from using Emacs for everything, sometime around 2000, I tried a number of things and ended up with [Eclipse](https://eclipseide.org/), which was at the time a fairly simple but fairly solid Java oriented integrated development environment (IDE). I stayed with Eclipse then for about a decade; but when I moved to mainly developing in Clojure, Eclipse just didn't do Clojure very well, I switched back to Emacs for a while, was driven mad by it again, and found LightTable as a blissful release; which takes us back to the beginning of this section. + +Last month, when I was searching for something to replace VSCodium and had realised once again how much I hate using Emacs for serious development, I tried Eclipse. + +It's... not awful? It's become a very polished, very configurable IDE; it has excellent facilities for C development. But I found it intrusively over-helpful: its continual 'helpful' suggestions got in my way. I used it for about ten days. I wasn't enjoying it. But what made me give up on it was because it won't follow your configured desktop colour theme, and I wasn't able to find a dark-mode theme for it that worked for me: there are plenty of themes , but they are only applied to the editing panels, not to the chrome or to any of the other panels. I find white backgrounds really unpleasant on my eyes. + +#### KDevelop and Gnome Builder + +I know I tried [KDevelop](https://kdevelop.org/) at some stage in this process. I can't remember why I rejected it. There's probably a reason. I also tried [Gnome Builder](https://apps.gnome.org/en-GB/Builder/) and rejected it very quickly, again I can't remember why; having a wee play with it just now it feels quite nice, and I may have another try. However, the Debian package of Gnome Builder [does not include the help files](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1111418), and, without them, I haven't found out how to invoke the debugger. + +#### Basic text editors + +I obviously have a basic text editor, [gedit](https://gedit-text-editor.org/), on my system. It does C syntax highlighting very well, but doesn't do code completion, and doesn't have any integration with a build system or debugger. I have various debugger user interfaces — I like [seergdb](https://github.com/epasveer/seer) — but I do have it convenient to have a debugger integrated into my editor, rather than having to switch between two separate applications. Similarly, it's convenient to have a terminal integrated with the development environment, although it doesn't need to be. GEdit, plus seergdb, plus a terminal, plus some sort of a git browser, would work for me. + +#### New editors + +People online have suggested I try two new editors: [Zed](https://zed.dev/) and Gram: these are essentially the same editor, in fact. Zed proudly announces itself as + +> a minimal code editor crafted for speed and collaboration with humans and AI + +The Zed project seems to want to monetise their work by selling you AI tokens. Which LLM is behind their AI I don't know. Open Source development needs to be funded somehow; funding it through a tax on people who use AI is as good a way as any. + +Dear reader, I do **not want** to collaborate with AI; I don't want any of that shit in my working environment. So that immediately got my back up. It also doesn't have a Debian installer. But I was able to build it from source, and have been using it consistently over the last couple of days, and it's very pleasant. There's a built in debugger, but I cannot get it to work. Beyond that, my build crashes occasionally — maybe once every two or three hours; but it doesn't seem to lose anything when it crashes, so this is not obnoxious. If I ignore the 'AI' features, the lack of a working debugger is the only mark against it. + +[Gram](https://gram.liten.app/) is said to be a fork of Zed with the AI features removed. It has a proper Debian installation repository, which is a significant step up over Zed. Unfortunately, it won't run on my desktop machine, due to [a problem with the video card](https://codeberg.org/GramEditor/gram/issues/256). On my laptop, it runs fine, and seems generally usable — although, again, I can't get the debugger to work. + +#### Conclusion for now + +The conclusion for now is that I don't have a conclusion for now. Any of Gnome Builder, Zed and Gram are sort of good enough. Zed crashes, which is not desirable; Gram only launches on my laptop, but I mostly do serious development on my desktop; I can't yet work out how to launch the debugger on any of them. But none are annoying, none get in my way. I'll keep on evaluating. ## 20260503 diff --git a/src/c/environment/function_bindings.c b/src/c/environment/function_bindings.c new file mode 100644 index 0000000..00d026c --- /dev/null +++ b/src/c/environment/function_bindings.c @@ -0,0 +1,175 @@ +/** + * environment/function_bindings.c + * + * Post Scarcity Software Environment: + * + * Provide bindings for substrate functions. At least in theory, these + * bindings only need to be initialised on node zero. + * todo: they really ought to be in a namespace ::system:bootstrap, once I + * have namespaces and paths working. + * + * (c) 2026 Simon Brooke + * Licensed under GPL version 2.0, or, at your option, any later version. + */ + +#include +#include + +#include "environment/privileged_keywords.h" +#include "memory/node.h" +#include "memory/pointer.h" +#include "memory/tags.h" + +#include "ops/assoc.h" +#include "ops/bind.h" +#include "ops/cond.h" +#include "ops/eval_apply.h" +#include "ops/eq.h" +#include "ops/inspect.h" +#include "ops/stack_ops.h" +#include "ops/string_ops.h" + +#include "payloads/cons.h" +#include "payloads/function.h" +#include "payloads/special.h" + +/** + * Bind this compiled `executable` function, as a Lisp function, to + * this name in the `oblist`. + * \todo where a function is not compiled from source, we could cache + * the name on the source pointer. Would make stack frames potentially + * more readable and aid debugging generally. + */ + +struct pso_pointer +bind_function(struct pso_pointer frame_pointer, wchar_t *name, wchar_t *doc, + struct pso_pointer (*executable)(struct pso_pointer)) { + struct pso_pointer result = fetch_env(frame_pointer); + struct pso_pointer n = c_string_to_lisp_symbol(frame_pointer, name); + struct pso_pointer d = c_string_to_lisp_string(frame_pointer, doc); + + struct pso_pointer meta = + make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_bootstrap, nil), + make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_name, n), + make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_documentation, d), nil))); + + struct pso_pointer r = make_function(frame_pointer, meta, executable); + + if (!exceptionp(r)) { + result = make_cons( frame_pointer, make_cons( frame_pointer, n, r), result); + } + + return result; +} + +/** + * Bind this compiled `executable` function, as a Lisp special form, to + * this `name` in the `oblist`. + */ +struct pso_pointer +bind_special(struct pso_pointer frame_pointer, wchar_t *name, wchar_t *doc, + struct pso_pointer (*executable)(struct pso_pointer)) { + struct pso_pointer result = fetch_env(frame_pointer); + struct pso_pointer n = c_string_to_lisp_symbol(frame_pointer, name); + struct pso_pointer d = c_string_to_lisp_string(frame_pointer, doc); + + struct pso_pointer meta = + make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_bootstrap, nil), + make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_name, n), + make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_documentation, d), nil))); + + struct pso_pointer r = make_special(frame_pointer, meta, executable); + + if (!exceptionp(r)) { + result = make_cons( frame_pointer, make_cons( frame_pointer, n, r), result); + } + + return result; +} + +struct function_data { + char32_t *name; + char32_t *documentation; + void* executable; +}; + +/* right, the problem with all those pretty '#ifdefs' which might allow us to + * simply switch functions on and off just by including or not including .h + * files is that the C compiler is too primitive to know how many items there + * are in an array. So this number must be edited manually, and must be right. + */ +#define N_FUNCTION_INITIALISERS 4 + +/** initialisers for functions */ +struct function_data function_initialisers[] = { +#ifdef __psse_ops_assoc_h + {U"assoc", + U"(assoc key store): search `store` for the value associated with " + U"`key`.", + &assoc}, +#endif +#ifdef __psse_ops_bind_h + {U"bind!", + U"(bind! key value store): bind `key` to `value` in this store, modifying " + U"the store if it is writable to the user, otherwise returning a new " + U"store", + &bind}, +#endif +#ifdef __psse_ops_eq_h + {U"eq", + U"(eq args...): shallow, cheap equality; returns `t` if all `args...` " + U"are the same object, else `nil`.", + &eq}, + {U"equal", + U"(equal a b): expensive, deep equality: returns `t` if objects `a` " + U"and `b` have recursively equal value.", + &equal}, +#endif +#ifdef __psse_ops_eval_apply_h + // TODO: there's a lot of other stuff in eval_apply.c, which ought to be in + // other files but at present isn't. + {U"apply", + U"(apply fn args...): apply this `fn` to these `args...` and return " + U"their value.", + &lisp_apply}, + {U"eval", + U"(eval expression): evaluate this `expression` and return its value", + &lisp_eval}, +#endif +#ifdef __psse_ops_inspect_h + { + U"inspect", + U"(inspect expr), (inspect expr write-stream): inspect one complete " + U"lisp expression and return `nil`. If `write-stream` is specified and " + U"is a write stream, then print to that stream, else to the stream " + U"which is the value of `*out*` in the environment.", + &lisp_inspect + }, +#endif +}; + +/* right, the problem with all those pretty '#ifdefs' which might allow us to + * simply switch functions on and off just by including or not including .h + * files is that the C compiler is too primitive to know how many items there + * are in an array */ +#define N_SPECIAL_INITIALISERS 1 + +/** initialisers for special forms */ +struct function_data special_initialisers[] = { +#ifdef __psse_ops_cond_h + {U"cond", + U"(cond clauses...): conditional. Each `clause` is expected to be a " + U"list; if the first item in such a list evaluates to non-nil, the " + U"remaining items in that list are evaluated in turn and the value of " + U"the last returned. If no arg `clause` has a first element which " + U"evaluates to non nil, then nil is returned", + &lisp_cond}, +#endif +}; + +struct pso_pointer +initialise_function_bindings(struct pso_pointer frame_pointer) { + struct pso_pointer result = fetch_env(frame_pointer); + + return result; +} \ No newline at end of file diff --git a/src/c/environment/function_bindings.h b/src/c/environment/function_bindings.h new file mode 100644 index 0000000..e69de29 diff --git a/src/c/environment/privileged_keywords.c b/src/c/environment/privileged_keywords.c index 411e6a0..394fe09 100644 --- a/src/c/environment/privileged_keywords.c +++ b/src/c/environment/privileged_keywords.c @@ -23,6 +23,13 @@ #include "ops/string_ops.h" +/** + * layer metadata for functions written in C + */ +struct pso_pointer privileged_keyword_bootstrap; + +struct pso_pointer privileged_keyword_documentation; + /** * location metadata for exceptions (and possibly location in other contexts). */ @@ -37,7 +44,9 @@ struct pso_pointer privileged_keyword_name; #define load_and_lock(var,val)var = lock_object(c_string_to_lisp_keyword(nil, val)) -struct pso_pointer initialise_privileged_keywords( struct pso_pointer env){ +struct pso_pointer initialise_privileged_keywords(struct pso_pointer env) { + load_and_lock(privileged_keyword_bootstrap, PK_BOOTSTRAP); + load_and_lock(privileged_keyword_documentation, PK_DOCUMENTATION); load_and_lock(privileged_keyword_location, PK_LOCATION); load_and_lock( privileged_keyword_name, PK_NAME); } \ No newline at end of file diff --git a/src/c/environment/privileged_keywords.h b/src/c/environment/privileged_keywords.h index 190c6e4..5726fb7 100644 --- a/src/c/environment/privileged_keywords.h +++ b/src/c/environment/privileged_keywords.h @@ -3,7 +3,7 @@ * * Post Scarcity Soctware Environment * - * TODO: Edit purpose. + * Keywords guaranteed to be present in the environment on each node. * * Copyright (c): 27 Apr 2026 Simon Brooke * Licensed under GPL version 2.0, or, at your option, any later version. @@ -11,11 +11,15 @@ #ifndef SRC_C_ENVIRONMENT_PRIVILEGED_KEYWORDS_H_ #define SRC_C_ENVIRONMENT_PRIVILEGED_KEYWORDS_H_ +#include "memory/pointer.h" +#define PK_BOOTSTRAP U"bootstrap" +#define PK_DOCUMENTATION U"documentation" #define PK_LOCATION U"location" #define PK_NAME U"name" -#include "memory/pointer.h" +extern struct pso_pointer privileged_keyword_bootstrap; +extern struct pso_pointer privileged_keyword_documentation; extern struct pso_pointer privileged_keyword_location; extern struct pso_pointer privileged_keyword_name; diff --git a/src/c/ops/assoc.c b/src/c/ops/assoc.c index 9e5672d..647b7bf 100644 --- a/src/c/ops/assoc.c +++ b/src/c/ops/assoc.c @@ -115,13 +115,8 @@ bool c_internedp( struct pso_pointer key, struct pso_pointer store ) { * @return a pointer to the value of the key in the store, or nil if not found */ struct pso_pointer assoc( -#ifndef MANAGED_POINTER_ONLY - struct pso4 *frame, -#endif struct pso_pointer frame_pointer ) { -#ifdef MANAGED_POINTER_ONLY struct pso4 *frame = pointer_to_pso4( frame_pointer ); -#endif struct pso_pointer key = fetch_arg( frame, 0 ); struct pso_pointer store = or( make_frame( 2, frame_pointer, fetch_arg( frame, 1 ), diff --git a/src/c/ops/assoc.h b/src/c/ops/assoc.h index 746a6ea..1fcf981 100644 --- a/src/c/ops/assoc.h +++ b/src/c/ops/assoc.h @@ -16,6 +16,8 @@ #include "memory/pointer.h" +struct pso_pointer assoc(struct pso_pointer frame_pointer); + struct pso_pointer search( struct pso_pointer key, struct pso_pointer store, bool return_key ); diff --git a/src/c/ops/cond.c b/src/c/ops/cond.c index c600d98..f2949d4 100644 --- a/src/c/ops/cond.c +++ b/src/c/ops/cond.c @@ -8,6 +8,7 @@ #include "memory/node.h" #include "memory/pointer.h" #include "memory/pso2.h" +#include "memory/pso4.h" #include "memory/tags.h" #include "ops/eval_apply.h" @@ -29,12 +30,12 @@ */ struct pso_pointer eval_cond_clause( struct pso_pointer clause, struct pso4 *frame, - struct pso_pointer frame_pointer, - struct pso_pointer env ) { - struct pso_pointer result = nil; + struct pso_pointer frame_pointer) { + struct pso_pointer result = nil; + struct pso_pointer env = fetch_env(frame_pointer); #ifdef DEBUG - debug_print( L"\n\tCond clause: ", DEBUG_EVAL, 0 ); + debug_print( U"\n\tCond clause: ", DEBUG_EVAL, 0 ); debug_print_object( clause, DEBUG_EVAL, 0 ); debug_println( DEBUG_EVAL); #endif @@ -49,19 +50,19 @@ struct pso_pointer eval_cond_clause( struct pso_pointer clause, c_progn( frame, frame_pointer, c_cdr( clause ), env ) ); #ifdef DEBUG - debug_print( L"\n\t\tCond clause ", DEBUG_EVAL, 0 ); + debug_print( U"\n\t\tCond clause ", DEBUG_EVAL, 0 ); debug_print_object( clause, DEBUG_EVAL, 0 ); - debug_print( L" succeeded; returning: ", DEBUG_EVAL, 0 ); + debug_print( U" succeeded; returning: ", DEBUG_EVAL, 0 ); debug_print_object( result, DEBUG_EVAL, 0 ); debug_println( DEBUG_EVAL); } else { - debug_print( L"\n\t\tCond clause ", DEBUG_EVAL, 0 ); + debug_print( U"\n\t\tCond clause ", DEBUG_EVAL, 0 ); debug_print_object( clause, DEBUG_EVAL, 0 ); - debug_print( L" failed.\n", DEBUG_EVAL, 0 ); + debug_print( U" failed.\n", DEBUG_EVAL, 0 ); #endif } } else { - result = throw_exception( c_string_to_lisp_symbol( frame_pointer, L"cond" ), + result = throw_exception( c_string_to_lisp_symbol( frame_pointer, U"cond" ), c_string_to_lisp_string (frame_pointer, L"Arguments to `cond` must be lists" ), frame_pointer ); @@ -78,14 +79,10 @@ struct pso_pointer eval_cond_clause( struct pso_pointer clause, * * * (cond clauses...) * - * @param frame my stack frame. - * @param frame_pointer a pointer to my pso4. - * @param env the environment in which arguments will be evaluated. * @return the value of the last expression of the first successful `clause`. */ -struct pso_pointer -lisp_cond( struct pso4 *frame, struct pso_pointer frame_pointer, - struct pso_pointer env ) { +struct pso_pointer lisp_cond(struct pso_pointer frame_pointer) { + struct pso4 *frame = pointer_to_pso4(frame_pointer); struct pso_pointer result = nil; bool done = false; @@ -97,7 +94,7 @@ lisp_cond( struct pso4 *frame, struct pso_pointer frame_pointer, // to nil, the form still succeeded and we should still exit `cond`. // - result = eval_cond_clause( clause_pointer, frame, frame_pointer, env ); + result = eval_cond_clause( clause_pointer, frame, frame_pointer ); if ( !c_nilp( result ) && c_truep( c_car( result ) ) ) { result = c_cdr( result ); @@ -106,7 +103,7 @@ lisp_cond( struct pso4 *frame, struct pso_pointer frame_pointer, } } #ifdef DEBUG - debug_print( L"\tCond returning: ", DEBUG_EVAL, 0 ); + debug_print( U"\tCond returning: ", DEBUG_EVAL, 0 ); debug_print_object( result, DEBUG_EVAL, 0 ); debug_println( DEBUG_EVAL); #endif diff --git a/src/c/ops/cond.h b/src/c/ops/cond.h new file mode 100644 index 0000000..a2f7136 --- /dev/null +++ b/src/c/ops/cond.h @@ -0,0 +1,20 @@ +/** + * ops/cond.h + * + * Post Scarcity Software Environment: cond. + * + * cond a name to a value in a store. + * + * (c) 2026 Simon Brooke + * Licensed under GPL version 2.0, or, at your option, any later version. + */ + +#ifndef __psse_ops_cond_h +#define __psse_ops_cond_h + +#include "memory/pointer.h" + + +struct pso_pointer lisp_cond( struct pso_pointer frame_pointer ); + +#endif diff --git a/src/c/ops/eq.c b/src/c/ops/eq.c index f350d5a..d7b4f38 100644 --- a/src/c/ops/eq.c +++ b/src/c/ops/eq.c @@ -131,7 +131,7 @@ struct pso_pointer eq( * * symbols * * strings * - * * (equal? arg1 qrg2) + * * (equal? arg1 arg2) * * @return `t` if all args are pointers to the same object, else `nil`; */ diff --git a/src/c/ops/eval_apply.c b/src/c/ops/eval_apply.c index 0473523..361c911 100644 --- a/src/c/ops/eval_apply.c +++ b/src/c/ops/eval_apply.c @@ -260,8 +260,7 @@ struct pso_pointer eval_forms( struct pso_pointer frame_pointer ) { * * This is experimental. It almost certainly WILL change. */ -struct pso_pointer lisp_try( struct pso_pointer frame_pointer, - struct pso_pointer env ) { +struct pso_pointer lisp_try( struct pso_pointer frame_pointer) { struct pso_pointer result = nil; struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer body_frame = @@ -301,8 +300,7 @@ struct pso_pointer lisp_try( struct pso_pointer frame_pointer, * @return the root namespace. */ struct pso_pointer -lisp_oblist( struct pso4 *frame, struct pso_pointer frame_pointer, - struct pso_pointer env ) { +lisp_oblist( struct pso4 *frame, struct pso_pointer frame_pointer) { return oblist; } @@ -750,9 +748,6 @@ struct pso_pointer lisp_apply( struct pso_pointer frame_pointer) { * * * (eval expression) * - * @param frame my pso4. - * @param frame_pointer a pointer to my pso4. - * @param env my environment. * @return * * If `expression` is a number, string, `nil`, or `t`, returns `expression`. * * If `expression` is a symbol, returns the value that expression is bound diff --git a/src/c/ops/inspect.c b/src/c/ops/inspect.c index ee64388..9d3ce60 100644 --- a/src/c/ops/inspect.c +++ b/src/c/ops/inspect.c @@ -21,10 +21,7 @@ #include "ops/stack_ops.h" /** - * Function: dump/inspect one complete lisp expression and return nil. If - * write-stream is specified and is a write stream, then print to that stream, - * else the stream which is the value of - * `*out*` in the environment. + * Function: dump/ * * * (inspect expr) * * (inspect expr write-stream) diff --git a/src/c/ops/inspect.h b/src/c/ops/inspect.h index 6803e09..800d643 100644 --- a/src/c/ops/inspect.h +++ b/src/c/ops/inspect.h @@ -9,8 +9,8 @@ * Licensed under GPL version 2.0, or, at your option, any later version. */ -#ifndef psse_ops_inspect -#define psse_ops_inspect +#ifndef psse_ops_inspect_h +#define psse_ops_inspect_h #include "memory/pointer.h" diff --git a/src/c/payloads/function.c b/src/c/payloads/function.c new file mode 100644 index 0000000..14015ab --- /dev/null +++ b/src/c/payloads/function.c @@ -0,0 +1,25 @@ +/** + * payloads/function.c + * + * an ordinary Lisp function - one whose arguments are pre-evaluated. + * + * (c) 2026 Simon Brooke + * Licensed under GPL version 2.0, or, at your option, any later version. + */ + +#include "memory/pointer.h" +#include "memory/pso.h" +#include "memory/pso2.h" +#include "memory/tags.h" + +struct pso_pointer make_function( + struct pso_pointer frame_pointer, struct pso_pointer meta, + struct pso_pointer (*executable)(struct pso_pointer frame_pointer)) { + struct pso_pointer result = allocate(frame_pointer, FUNCTIONTAG, 2); + struct pso2 *object = pointer_to_object(result); + + object->payload.function.meta = meta; + object->payload.function.executable = executable; + + return result; +} diff --git a/src/c/payloads/function.h b/src/c/payloads/function.h index 419ffa7..8c7da98 100644 --- a/src/c/payloads/function.h +++ b/src/c/payloads/function.h @@ -39,4 +39,8 @@ struct function_payload { struct pso_pointer ( *executable ) ( struct pso_pointer frame_pointer ); }; +struct pso_pointer make_function( + struct pso_pointer frame_pointer, struct pso_pointer meta, + struct pso_pointer (*executable)(struct pso_pointer frame_pointer)); + #endif diff --git a/src/c/payloads/special.c b/src/c/payloads/special.c new file mode 100644 index 0000000..abf8d97 --- /dev/null +++ b/src/c/payloads/special.c @@ -0,0 +1,25 @@ +/** + * payloads/special.c + * + * a special Lisp function - one whose arguments are **not** pre-evaluated. + * + * (c) 2026 Simon Brooke + * Licensed under GPL version 2.0, or, at your option, any later version. + */ + +#include "memory/pointer.h" +#include "memory/pso.h" +#include "memory/pso2.h" +#include "memory/tags.h" + +struct pso_pointer make_special( + struct pso_pointer frame_pointer, struct pso_pointer meta, + struct pso_pointer (*executable)(struct pso_pointer frame_pointer)) { + struct pso_pointer result = allocate(frame_pointer, SPECIALTAG, 2); + struct pso2 *object = pointer_to_object(result); + + object->payload.special.meta = meta; + object->payload.special.executable = executable; + + return result; +} diff --git a/src/c/payloads/special.h b/src/c/payloads/special.h index 5ccdb1f..ef913e9 100644 --- a/src/c/payloads/special.h +++ b/src/c/payloads/special.h @@ -18,8 +18,12 @@ /** * A special form - one whose arguments are not pre-evaluated but passed as - * provided. + * provided. Shares payload with function, q.v. * \see NLAMBDATAG. */ +struct pso_pointer make_special( + struct pso_pointer frame_pointer, struct pso_pointer meta, + struct pso_pointer (*executable)(struct pso_pointer frame_pointer)); + #endif