By: Simon Brooke :: 5 May 2026
20260505
The stack frame corruption(?) bug
I have a weird bug in read_symbol, which at present I'm not understanding (now fixed: read on).
Stack frames in 0.1.0 are 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
(z y x)
Obviously, this 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:
/**
* @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:
<exception: ("Invalid object in sequence")>
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:
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 (edited: I'm wrong. Zed, and so presumably also Gram, use lldb). 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'. 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 one 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:
{
// 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 is better. Using it, I was able to compose this stanza:
{
"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!
Admittedly, she doesn't yet do much, but...
Evaluating editors
My favourite Clojure editor, LightTable, went dark — or at least, ceased to be actively developed — about five years ago; and as it depends on libraries which are not available in Debian Trixie, the published executable will no longer run. At about the time it died I did have a look at whether it would be feasible for me to take over maintenance of it, and I came to the conclusion that it would be too much work.
VS Codium
So I switched to VSCodium, which is a fork of Microsoft's supposedly open source VS Code editor with all the proprietary Microsoft shit taken out, some years ago. VS Codium, like VS Code, is built on Electron, which means it's built, fundamentally, on a JavaScript library stack, with all the instability and insecurity that implies. I have been getting increasingly nervous about my use of VSCodium in the light of increasingly frequent attacks on the JavaScript ecosystem.
This is not to say I dislike VSCodium; I don't. It's been, mainly, a pleasure to use. It's stable, it doesn't get in my way, it's highly configurable and extensible. I just don't have the bandwidth to monitor and audit the libraries it is using.
Emacs
In April had one of my periodic attempts to switch back to GNU Emacs — that ancient editor which is Generally Not Used Except by Middle Aged Computer Scientists. Back in the day I didn't use Emacs for editing Lisp, of course, because back in the day I was using real Lisps like Portable Standard Lisp and InterLisp which had built in structure editors. But I used to use Emacs for almost everything else, including reading my mail, browsing Usenet, and editing shell scripts and programs in the languages of οἱ πολλοί. And given that the substrate of Post Scarcity is (still) being written in C, just as KnacqTools was back in the day, why not Emacs? After all, it is extremely stable, and extraordinarily configurable and extensible.
The answer, dear reader, is that Emacs is determined to get in my way in every possible way. It is obnoxious to use. Every key binding, every mouse action, which works in every other software package on a modern windowed user interface does something completely different in Emacs (and vice versa). Your muscle memory no longer works. Every keystroke, every command action, has to be carefully thought about. You have two choices: you can switch entirely to living only in Emacs and relearning the Emacs keybindings, or to live in a permanent hell of confusion, overthinking and self-doubt. And, in this day and age, there are many things which Emacs does not do nearly so well as more modern packages do. You can browse the web in Emacs — of course you can! — but, dear reader, you really wouldn't want to.
Eclipse
When I finally switched away from using Emacs for everything, sometime around 2000, I tried a number of things and ended up with Eclipse, 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 at some stage in this process. I can't remember why I rejected it. There's probably a reason. I also tried Gnome 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, and, without them, I haven't found out how to invoke the debugger.
Basic text editors
I obviously have a basic text editor, gedit, 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 — but I do find 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 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 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. 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.
End of the day (21:22)
read_symbol is breaking horribly, and a cursory glance at the code shows multiple things wrong. But the first thing wrong is that I'm not sanity-checking the arguments; and that's key because it seems that somehow the stream is getting spliced into what should be a stream of characters. That's the first place to start looking for trouble in the morning.
20260503
Right, so, it's a week since my last entry. The version of eval/apply copied from 0.0.6 still doesn't compile, let alone work. There are reasons. I've been ill — my brain really is fucked — and I've had outdoor work it's felt urgent to do.
There is progress. I am cleaning up bits of old cruft as I go. But I don't think copying the old code was a good decision. Probably, if I had started a clean room implementation a week ago, I would now have a working evaluator. Certainly, I'd have a better one.
Probably, the first thing I should do when I get the old one working is write a new, clean, one.
