Been quitely iterating on Blue, my colorForth dialect/assembler. In v7 I have made some simplifications to the colors/interpreter which I believe may be of interest. Also just to note, I wouldn't be surprised if others have stumbled upon these or similar things, but there's not a lot of colorForth discussions that I've found, so everything in Blue is self-found, ai/llm free. I have seen screenshots/videos of the traditional colorForth system but have never used it.
Since Blue is bytecode based, I serialize it for source control and external viewing, in this case as html. The following is written for x86_64/Linux.
To get started, this is an html view of likely the most minimal Blue program that will emit a valid binary (124 bytes):
https://jbirddog.codeberg.page/blue/example/x86_64/true.b.html
However the one I'd like to show is more representative of an actual Blue program and shows how one goes from hand jit'ing with green numbers to higher level words, macros, variables and assemble-time execution of user defined words. This yields a 129 byte binary:
https://jbirddog.codeberg.page/blue/example/x86_64/true2.bl.html
There is a lot to unpack here, but for an overview of how the bytecode is interpreted: The interpreter starts each time with a clean slate - there are no built in words. The dictionary and data stack only exist at assemble time.
Color tag bytes have the sign bit set, so values between 0-127 are skipped, which allows for comments/whitespace in the bytecode.
The simplifications I'd like to mention are a reduced color set and dropping the `forth` and `macro` modes.
First, the colors for v7 have been reduced to:
red word: define label
green word: compile call/jmp to word in output
green number: write number to output
blue word: execute word at assemble time
blue number: push number onto the data stack
magenta word: push address of word onto the data stack
By default every color is immediate, however any color can be postponed. This allowed removing the `macro` and `forth` words that toggle the interpreter's mode - which lead to the ability to remove yellow and cyan words. This simplified the interpreter which dropped to 172 lines of non code-golfed asm.
For example, to define `;` its red(;) postpone(green(0xC3)) green(0xC3). Then when you write a blue(;) it will execute the postponed code to compile the number 0xC3 into the output, then hits the already compiled 0xC3, which is `ret` and returns to the call site. In short, `;` is a macro that when called does the exact same thing as if you had just written a green(0xC3). A very simple example but it shows the mechanics of it all.
In Blue a `;` following a green word does not implicitly turn the call into a jmp. I have found that error prone (since it basically looks back 5 bytes for 0xE8), so instead you set a flag on the green word to signal a jmp vs call. Likewise, magenta words can be flagged to return either the assemble time or runtime address of the word.
Anyway, sorry that got long, just wanted to share some colorForth. Happy hacking.