In a previous post, I talked a bit about my endeavour to build myself a lil' Lisp: PebbLisp. It's a project I work off and on depending on my motivation. Recently, I was pretty bored with it, and found myself looking for a new project. Having read a bit about Forth, I thought it wouldn't be too hard to try implementing it. Plus, this would be a great opportunity do some practice with Rust.
If you're not familiar with Forth, it's a weird little interpreted language built around a stack. Passing in a number will add it to the top of the stack, passing in a function will (usually) do something with numbers on the stack. For example, what happens when you run the following?
2 3 + .
3 are added to the stack. Then, + pops the top two items off
the stack, and adds them. Then it pushes the result back onto the stack.
Finally, the . pops the top item off of the stack, and prints it out:
You can also compile functions that add some of these elements, and the language can become surprisingly expressive:
: feet 12 * ;
: yards feet 3 * ;
5 feet .
\ This prints 60 (5 feet in inches)
8 yards .
\ This prints 288 (6 yards in inches)
5 yards 2 feet + .
\ This prints 204 (5 yards + 2 feet)
So, I figured "hey, looks simple enough", and got to work. Got basic parsing going, got the stack pretty functional, added a few functions, whoo. Rust turned out to be a great choice. Expressive, safe, and with built-in testing. Good stuff. Almost too good. I'd started looking at PebbLisp again. Could I write an interpreted language using my own interpreted language? Forth is pretty simple, conceptually. How hard could it be?
Actually, it hasn't been the unholy mess that I was expecting. Yes, my PebbLisp is severely lacking (there was no way to take user input before?). No, it has nothing anything even remotely resembling a formal language definition (I've operated on the "this seems to make sense" principle). With some tweaks though, it has kind of worked!
My to-be-named Forth (Forbble? PLForth? PebForListhble?) is incredibly buggy, and pretty tedious even when it does work, but it sort of kind of does!
$ "feet" 12 * "END"
$ "yards" 36 * "END"
5 "yards" 2 "feet" +
\ This prints 204
So, for reasons I've yet to suss out:
- Functions can't be defined using other functions
- Functions need to be defined using quotes, for goofy string/symbol reasons in PebbLisp.
- Statements that use a user-defined function seem to ignore the dot operator at the end.
Please ignore the awful END symbol it should be easy to fix.
Anyway, it's been pretty neat to actually try doing something with PebbLisp.
I've cleaned up a bunch of hacky ways of doing things, and added a few more.
Some of it is super useful outside of Forbble (Flisbble? Porlth?) too. Since I
eval function, and a way to take input, it's actually super easy to
write up a REPL in PebbLisp itself. Very fun!
PebbLisp was never intended to be a terrifically practical language. Have I mentioned it was originally created to be written and run on a smartwatch? Yeah, the Pebble (hence the name). Enjoy typing on its four buttons. This is also the reason PebbLisp is written in C, as nothing else worked with their SDK. Nonetheless, it is incredibly satisfying to get your own language running something even remotely real. Even if that something is another, even less useful language.
I love it.
If you, like me, enjoy programming enough to do it in your free time, try writing a little lang. Forth is a great place to start, and Rust is a great language to write in, but there are certainly others that would be cool to work with. Or just try using a different language. I'm not saying your Forth knowledge will get you hired anywhere, but it's a good excuse to look at your code differently, which is always a good idea.