Jonesforth – A sometimes minimal Forth compiler and tutorial (2007)
github.com
github.com
https://github.com/matematikaadit/jombloforth
If you like forth there's an awesome series of comments here on hacker news on building a simple variant in a few simple steps:
https://news.ycombinator.com/item?id=13082825
I took that, and built a simple forth-like system, in golang following the original recipe and breaking it down into simple steps for learning-purposes:
JonesForth could be more clear about the high-level logic of its interpreter part. I tried to make this part as clean as possible, hopefully did not miss anything.
Maybe I will make a RISCV version in my copious free time in the future.
[0] https://github.com/EarlGray/language-incubator/blob/29755c32...
I know I'm all talk right now, like you say, I need to manage my free time so that I would have the "copious free time" to work on this.
The only reason I was thinking Raspberry Pi was I wanted something that would be readily available to anyone (although, at the moment it's out of stock due to supply chain issues). I was actually looking at whether anything similar was available with RISC-V (like you mentioned) to target, since that would be interesting.
Not being familiar with this sense of “thread,” I found a good explainer here: https://www.complang.tuwien.ac.at/forth/threaded-code.html
And, as always, Wikipedia: https://en.m.wikipedia.org/wiki/Threaded_code
However Forth does allow you to "inline" words, basically form a new word by chaining together existing words, which avoids this overhead. This doesn't optimise adjacent words, but it still gets semi-reasonable performance. (Inlining in JONESFORTH: https://github.com/nornagon/jonesforth/blob/d97a25bb0b06fb58...)
Modern Forths just have regular optimising compilers so none of this stuff applies, but they don't have the simple purity of one that you write and fully understand yourself.
So many techniques that would improve performance on a 1980s processor can be woefully inefficient on a 21st century one. It's so easy to be still holding the computing model of the former in your head.
Super wrong. You're far better off precomputing the whole sprite bitmap -- even if you don't end up using it and doing a bulk operation to display it or not display it. Because doing that "if sprite is here?" in a loop is super super expensive, more expensive than just blitting the splits and not using them.
Most efficient ended up being precomputing things into boolean bitset vectors and using SIMD operations to do things based on those.
Even if not doing GPU stuff, the fastest way to compute these days is to think of everything in bulk bulk bulk. The hardware we have now is super efficient at vector and matrix operations, so try to take advantage of it.
(After doing this I have a hunch that a lot of the classic machine emulators <VICE, etc.> that are out there could be made way faster if they were rewritten in this way. )
In my benchmarks that are definitely not suitable to infer much, OpenJDK without JIT can perform 1.5-8x better, though of course the whole implementation is different so don’t read too much into that.
For example to compile : SQUARE DUP + ; all the compiler has to do is copy the machine code of DUP to the place where SQUARE is being compiled, remove the ret instruction at the end of it, and copy the machine code of + after it. It can also do some small optimizations to remove redundant instructions.
You can do this with other languages, but concatenative languages can make it as simple as literally concatenating bits of code.
ERROR: DUP + is not SQUARE. Did you mean DOUBLE?Relatively easily, you can get rid of the interpreter overhead by writing blocks of machine code that do each Forth word's action, instead of bytecode to dispatch the interpreter to each of those routines. Forth can be adapted for that easily enough, and some Forths do support this on a per-word basis, allowing you to pick how you want your words compiled.
Forth can also get the whole optimizing compiler treatment. Some optimizing Forth compilers have been released, but I don't know how good they were/are. Certainly never needed that kind of speed myself. I don't know if any Forth yet benefited from it, but a lot of work was put into Java, on approaches for optimizing stack machine-style code to reasonably fast native code for register machines.
https://git.sr.ht/~sforman/PythonOberon/tree/master/item/for...
https://git.sr.ht/~sforman/PythonOberon/tree/master/item/for...
The idea is to make something like (or a port of) CollapseOS, but with a 32-bit machine. The Oberon RISC chip is big enough to be more useful than 8-bit systems yet simple enough that you could build one out of discreet components if you had to. It's really really simple.
> This wiki is about bootstrapping, i.e., building up compilers and interpreters and tools from nothing.
We've had environments with far greater individual programmer productivity than is currently common. But while forth starts out nicely with simple systems, costs escalate unhappily with goal complexity. Similarly with smalltalk, lispms, prolog, haskell. The perennial "we'll easily do a full-stack rewrite in our wonderful X" efforts... haven't.
Past/current efforts avoid implausible goals, like LLVM-quality compilation has to become a one-person project. Or slather on viability constraints, like must run on M in time T, and be accessible to population P. Or "we don't know how to compile that leverage efficiently and consistently - that paper hasn't been written yet - so you can't have it".
But there are increasingly opportunities to do things differently now, if narrower goals demanded it. Community-wide compilation caches - "it ok that the type proof / bulk code translation / whatever sometimes takes days, it only needs to happen once". Compiler runs spinning up 10000 cloud instances. And so on.
What might it look like, to attempt not merely a bootstrap, but a subsuming breakaway?
We could just freeze all the fundamentals. If it was a real emergency, we could mostly stop working on new niche languages,Stop adding language features, stop deprecating stuff, and just focus on essential applications with only the tech we have.
We'd obviously want some level or new work on the fundamentals and language level stuff, but we'd just focus on things that help you code with less people and poorly trained people(Replacing C with Rust could be the big project of the century).
Tech is pretty great at the moment, seems like we could just get by for centuries, totally long enough to rebuild, if we just had what we have now, and enough hardware infrastructure to keep the fabs running. We don't really need a new kernel or a new graphics card architecture until society is stable again.
Oops, my bad - I was more trying to shape a peculiar apocalypse to highlight seemingly neglected opportunities for progress. Software tech has indeed tremendously improved over these last few decades - we're living years of dreams. But in some respects... it feels like US general aviation - systemic dysfunction leaving society crippled and stuck using decades-obsolete tech. Spewing cognitively-impairing poison (leaded avgas). Take a 1980's person familiar with smalltalk and lisp machines and OT factor and KMS hypertext, and set them down in front of modern hardware, describe it's power, then turn it on, and... one can imagine a reaction with at least some element of "WTF - such awesome power, but why is it so strikingly crippled?". Picture, I think it was, an early Rails demo talk at a conference, with an audience largely of Java-based web devs... and they were just stunned, awed by how much more productive it was. I suggest we've been repeatedly failing to execute on opportunities for such transitions. "Tech is pretty great[...] we could just get by for centuries"... :) Ha, it sometimes feels like we're working towards that. A story of "I crave a stack with these <empowering features> I've seen separately... but fear I will retire first, years from now, never having had it available". Another decade plus will be a half century since the '80s. You only get so many patent expiration and monopoly turnover cycles per century. But maybe a 2020's "Great AR Rewrite" might pressurize progress???
Unrelatedly (perhaps) I tried writing some basic software in forth and it was pretty horrific. Even something as simple as representing a variable length array feels like fighting the language
It might be instructive to compare FORTH to e.g. the previously discussed language K, which is similarly terse in some ways. It turns out that a few convenience features - including first-class- and higher-order- functions, neither of which really sit well within the FORTH stack-focused model - can make a real difference.
I think there's a gap between what you're trying to solve and what you think you're trying to solve, and that's giving a massive impedance mismatch between what you're trying to do and how you're trying to do it.
It’s a bit like saying the only thing anyone ever seems to do with Common Lisp is to make macros which redefine the syntax of language. How frustrating! We’ll if they didn’t want to make use of macros, why were they using lisp in the first place?
Part of the appeal of forth is making your own forth environment. That is the whole answer I think.
Edit: notably there are applications of forth which don’t involve reinventing the wheel, like Postscript and Bitcoin Script. In these domains devs aren’t so likely to engage in this activity.
Then what? Or is that the whole hobby?
But there are various actual products which result from efforts to create a minimal forth that are interesting. One of my favorites is the J1 CPU: https://www.excamera.com/sphinx/fpga-j1.html
Also I think many of these personal forths have explored interesting areas of programming language design, and when I have read about those which have been made public I often learn something new. It's like code poetry.
The last thing that causes the phenomenon you describe to happen is likely the culture around forth, likely as a result of the relative ease and utility of making domain-specific abstractions compared to general abstractions in forth. The community seems to not appreciate libraries and reusable code to the same extent as other ecosystems, preferring instead to share ideas, which each developer can then adapt to fit their particular project in a very minimalist manner instead of adopting some convoluted general solution.
It would be a PITA to strip it from its business-related stuff in order to publish it, so you will probably never see it online. I guess it's the same for other "serious" users.
> Unrelatedly (perhaps) I tried writing some basic software in forth and it was pretty horrific. Even something as simple as representing a variable length array feels like fighting the language
Yeah, don't fight the language, that's the key. And don't do stuff because that's what every other language is doing. "Solve the problem you've got!" Moore said. It could seem dead obvious, but it's actually difficult to prevent yourself from trying to solve problems you think you will have tomorrow, or from solving problems others have.
More specifically don't try to avoid using Forth variables because they are global and you've been taught that globals are absolute evil madness that eat kitties for breakfast. Instead, pick perhaps a couple of values that travel everywhere, causing "stack juggling" in your program, and make them global variables. Maybe hide those global variables behind regular words if you really really really intend to reuse parts of that program.
But really, solve the problem you have now. This also means you should make programs that will actually be useful for you. It can be difficult because this generally involves complicated stuff like connecting to a web server or GUI. Just be pragmatic and use native libraries (or whatever you can find that looks decent, gets the job done, and can be used by the Forth system you're using) to deal with the complicated crap others have been doing for decades. Forth hates complexity, any cheat is fair game.
1. https://www.excamera.com/files/j1demo/docforth/
2. https://www.excamera.com/sphinx/fpga-j1.html
Edit - also mentioned here by adastra22, I see.
Lisp is sort of the next step after Forth if you ask me, adding slightly more syntax to get better abstractions.
Forwards in time, backwards in abstraction level.
Thanks.