Ex:forth (Extensible FORTH)

Uhhh

Yes.

Ok, so this might need a bit more explanation. As you might have noticed, I quite like the FORTH programming language. In my previous entries, I uesed Gforth. Gforth is nice on paper, but has a few problems. Mainly, its developers kinda forgot that it's nice to make a release from time to time. Due to this, its C interop is broken in all packages I came accross and compiling it is not fun at all. (requires other Gforth installed, wants a bunch of packages (includeing TeX and GNU Emacs), has script only for few Linux package managers...)

I enjoy using FORTH together with Raylib, but I don't want everyone (so probably just me on other machines) to have to compile Gforth in order to use it.

I looked through other FORTHs, but no non-comertial FORTH I found supported C bindings at runtime. I don't want to give up tho, so I forked pforth, a small, portable FORTH written in C.

Goals

Currently my main goal is to be able to run gforth-tetris in it without doing any changes to it. For after that, I would like to add more Gforth words, but I want to reimplement them myself. First it's a good way to learn the language, and second, I would like to keep the 0BSD license. (Gforth uses GPL, as you might have guessed) I don't have problem with GPL in languages, but it just feels wrong to fork 0BSD code and make it GPL.

How is it going?

At firts, it's good to mentioned that pforth has builtin support for adding C words at compilation. This is not useful for me, as I don't want each program to use its own version of FORTH, but it helped a lot with implementing imports at runtime. Currently I support including from a custom C dymanic library, but automatic generation from FROTH is next to go.

Pforth has a 'CreateGlueToC' function that adds a word from C function at runtime. It has however two problems. It can only take up to 5 arguments and it must be in a specific array.

Adding support for more args was easy, so now it supports up to 15. (some Raylib functions take like 9 args I think, so it should be enough. Please, don't write C functions that take 15 args)

The array was a bit more of a problem, as I still wan't to keep the old option to extend Ex:forth at compile time. I also don't want to create the pointer in the extending file to keep at least some compatibility.

I switched to using a pointer that by default points to the array, but when first library is loaded, new space is allocated and the old array is copied to it. It does require user to manually specify the length of the array tho.

I'm not saying it's a good solution, but it's a solution.

How do I even get words from dynamic library at runtime? Well, firts of all I added a 'INCLUDE-CLIB' word. That uses the dlopen function to load the library.

The library has a function named 'addWords' that takes void* to custom function that adds words to the dictionary.

Well, that's it, I can include C libraries. Now just the FORTH generator.

...

Well, I also added a few more features.

First of all, I made Ex:forth include files relative to currently included file and not user position in the filesystem. I'm not sure why pforth doesn't have this, but I guess it's mainly intended for microcontrollers and such. (like most FORTHs) It was surprisingly easy, given it's working with strings in C. I did however have to wrap 'FILE*' in my own structure to store its relative name.

Second, I added a few Gforth words for invoking the shell and 'SOURCEFILENAME'. I will need this later. I also changed it so it falls back to REPL after finishing when starting with a file as a argument. With this I also changed 'BYE' to not throw error messages when called.

Last I also added my own custom words to determine current OS. This will be useful for shared library inclusion.

Notes

Here is where I would put any interesting code pices and knowledge that I have, but this was mostly just standard C code. Nothing much to see here.

Maybe just that C stores pointer length at dynamic allocation right before the pointer, so that it knows how much to free. You probably already knew that if you work in C tho...

It's also nice how some functions take buffer to use and then return it, so that you can still use the buffer itself, but it can also be nested inside other function calls.

C is good...