8 ms·
Implementing a Forth
- jshprentz 1y agoI went down the "make your own Forth" rabbit hole about 45 years ago. In January 1979, Byte Magazine's Language Forum contained the article, "IPS, An Unorthodox High Level Language."[1] The article described IPS, a language based on Forth, but with the word names translated to German. Thus, Forth's SWAP became VERT, short for vertauschen. The intriguing article concluded with a reference to Charles Moore's 1974 paper, "FORTH, a New Way to Program a Minicomputer,"[2] which was discussed on HN in 2022.[3] I had recently assembled a Quest Electronics Super Elf computer[4] with an expanded memory size of 4 KB. The IPS article mentioned implementations under 6 KB for 8080, 6502, and RCA COSMAC microprocessors, so I thought Forth might fit. The Super Elf included an 1861 video display controller chip, with a resolution of 64 x 128 pixels (big pixels!). I designed a font of 3 x 5 pixel characters to provide 21 lines of 16 characters. Good luck distinguishing M, N, H, U, and W without some context. I bought a (possibly surplus) keyboard from Radio Shack and screwed it onto a wedge of wood to achieve a usable typing angle. Moore's paper described about 75 Forth words. I wrote them on index cards and jotted down Forth definitions or RCA 1802 assembly code. I wrote an 1802 assembler in Fortran to ease the conversion into 1802 machine code. I still have the printouts and punch cards in storage. Development proceeded slowly. During a break at work, I would punch my assembly code for a few Forth words, run the assembler, and bring home the printout. That night I would load previous work into the Super Elf from cassette tape, key in the new words (and changes) via the Super Elf's hex keypad, and save back to another cassette tape. Then I would test the new Forth words and note any changes needed on the printout. Lather, rinse, and repeat until, at last, it all worked. The finished Forth system consumed a little more than 3 KB of the 4 KB of memory. User programs, data, and Forth stacks occupied the remaining memory. The R key stuck. The @ symbol was a 3 x 5 pixel blob (this is the very important Forth memory fetch operator). But it worked! I demonstrated the system at the local personal computer club--spun off from the local ham radio club. Based on their enthusiasm, I advanced to developing tinyForth[5] for the TRS-80, a story for another day. [1] https://archive.org/details/BYTE-MAGAZINE-COMPLETE/197901_Byte_Magazine_Vol_04-01_Life_Algorithms/page/146/mode/1up https://archive.org/details/BYTE-MAGAZINE-COMPLETE/197901_By... [2] https://articles.adsabs.harvard.edu/pdf/1974A%2526AS...15..497M https://articles.adsabs.harvard.edu/pdf/1974A%2526AS...15..4... [3] https://news.ycombinator.com/item?id=33134663 https://news.ycombinator.com/item?id=33134663 [4] https://www.oldcomputermuseum.com/super_elf.html https://www.oldcomputermuseum.com/super_elf.html [5] https://archive.org/details/80-microcomputing-magazine-1980-02/page/n15/mode/1up https://archive.org/details/80-microcomputing-magazine-1980-...
- throwaway71271 1y agoWow! What an amazing story! I started writing forth few months ago, wrote few interpreters, with jit or with types etc, and its just amazing, I think anyone should do it. TBH I don't think any other exercise has thought me as much about programming as this. I also notice the "return" of Forth, as it is probably the easiest high level language to make for computers with addressable memory and fetch execute cycle. The parser is just few lines of assembly and of course you can write the parser in the inner interpreter's bytecode, you don't even need assembly :) So hobbyists can just "make it" and make their own tiny operating systems with it. Of course everyone makes their own dialect, but I think thats OK. Things like https://github.com/howerj/lfsr https://github.com/howerj/lfsr LFSR CPU/VM running Forth, or UXN or duskos/collapseos. Now you can also use language models to help you onboard into the language, it do some practice programs and rewrite one program in many ways. So if you are young or old and never tried to Forth, don't miss out, its super fun.
- anthk 1y agoHowerj has a really easy subleq/muxleq interpreter. Eforth under Muxleq runs fast enough (perfectly usable) under an ATOM n270. Subleq too, but Muxleq it's must faster.
- 0x445442 1y agoWhat kind of programs would one naturally reach for Forth as the optimal solution? It has always struck me as a very low level language but I rarely hear this caveat from its advocates.
- rwmj 1y agoI highly doubt it's useful these days on general purpose computers, where even the very smallest ones can easily implement C (or better) or even run a whole operating system like Zephyr or Yocto. However if you're creating your own computer from scratch or have other artificial constraints (like it needs to fit into a boot sector[1]), and if you want to really understand precisely how your language works down to the lowest level detail while still having high-ish level constructs, then Forth is the ideal small language for that. [1] https://github.com/cesarblum/sectorforth https://github.com/cesarblum/sectorforth
- s-macke 1y agoNot sure if this counts as an implementation, but this is my Forth compiler, written in the high-level language Go [0]. This is also a challenge, as Forth usually requires machine language, call stack access, and is naturally written in assembler. Took several tries until I found working data structures. The funniest thing is, you can easily write and invent your own control structures in Forth, as well as change the behavior existing ones such as the do/while loop. \ alter the behavior of do by printing the current index in each loop. : do immediate [COMPILE] do \ first compile previous do impl. ' i , \ then print current index ' . , ; [0] https://github.com/s-macke/Forthly https://github.com/s-macke/Forthly
- astrobe_ 1y ago> This is also a challenge, as Forth usually requires machine language, call stack access, and is naturally written in assembler. Not really. Many implementations indeed do that, but only in pursuit of performance - the weakness of interpreted languages is speed. Otherwise one can just implement it as a bytecode interpreter, like e.g. Lua.
- s-macke 1y agoMy code uses some kind of very abstract byte code in a global heap []any structure (threaded code). "any" can be different types including Go functions. But most of them are pointers as usual in Forth.
- ofalkaed 1y agoI think for most people they would be better off developing their Forth in a high level language and writing their own software stacks and the core words. Once you have that start implementing other features in that high level language instead of in yourth Forth, add functions and scope, OOP, data structures, anything you can think of, you will learn a great deal about programming languages, their design and how they work. Doing things the Forth way of a minimal Forth and building everything out in Forth is useful for those who want to learn the low level stuff but once you have implemented that minimal Forth, most of what you learn is Forth.
- whobre 1y agoI am under impression that more people implement Forth than use it for programming...
- dcreager 1y agoThat's okay, it's a fun thing to do!
- rep_lodsb 1y agoA great article about why this might be: https://yosefk.com/blog/my-history-with-forth-stack-machines.html https://yosefk.com/blog/my-history-with-forth-stack-machines...
- desireco42 1y agoIt is educational thing to do. Like Lisp as well.
- anthk 1y agoWith Lisp you have beasts like Guile and Guix with Scheme and with Common Lisp, Maxima, Nyxt and even weird stuff like K3D or whatever was called where you could build 3D scenes while programming. A la PovRay, but with Common Lisp and in-place editing instead of batch rendering.
- juancn 1y agoIt's a great exercise as a programmer to implement a language, Forths and Lisps are popular choices. It greatly helps conceptualizing computation and what's truly essential at it's core.
- kabdib 1y agowe used FORTH extensively in the bring-up of the Atari ST. it was easy for the hardware engineers to come up with little fragments of FORTH so they could exercise their chips without needing much support from the software team FORTHs are fun to write. they'll teach you a lot about ruthless simplicity i don't think FORTH is useful for large programs, but that's not the point of the language
- fjfaase 1y agoI am currently implementing a stack based language as an intermediate language for a C compiler that I am developing. I have developed a relatively straight forward compiler for the stack language to the M1 assembler from live-bootstrap. This stack language has local variables. It actually makes use of two stacks: One in the traditional sense and one for the local variables (and the return address for calls). It reads as a kind of very primitive post-fix C kind of language. See: https://github.com/FransFaase/MES-replacement https://github.com/FransFaase/MES-replacement and https://www.iwriteiam.nl/D2506.html#2b https://www.iwriteiam.nl/D2506.html#2b
- peter_d_sherman 1y agoOne of the best articles on tiny Forth interpreters that I've seen on HN for a long time, with many excellent links for further study!
- john-tells-all 1y agoI adore the `Threaded Interpretive Languages` book! It very clearly describes the internals of a real language, and variations on how to get it all done. https://archive.org/details/R.G.LoeligerThreadedInterpretiveLanguagesTheirDesignAndImplementationByteBooks1981 https://archive.org/details/R.G.LoeligerThreadedInterpretive...
- anadem 1y agoYes, it's a lovely book and led me to my first paid project over 40 years ago! Using the book I built a Forth to monitor the local water authority systems, with water-level tracking and customer accounting & billing, implemented on a home-brewed network of North Star PCs. Iirc there's a minor bug in one of the base subroutines as written though now I don't remember what it was. But I still have the book :-)
- michelpp 1y agoMastering Forth is also an excellent book and gets deep into the subject with very clear writing and excellent diagrams. https://archive.org/details/mastering-forth-by-anderson-anita-tracy-martin-z-lib.org https://archive.org/details/mastering-forth-by-anderson-anit...
- codr7 1y agoMy first interpreters were Forth-derivatives, it's a great way to start designing your own languages. I've recently been working on a project to lower the barrier to entry for budding language designers. The language is strictly prefix, but could easily be turned into postfix with minor changes. https://github.com/codr7/shi https://github.com/codr7/shi
- curtisszmania 1y ago[dead]
- acidx 1y agoI recently implemented a Forth, compatible with the Forth Haiku dialect used by https://forthsalon.appspot.com/ https://forthsalon.appspot.com/ -- it uses a tail call/continuation-passing dispatching method, and performs some rudimentary optimizations. At some point it also spit out some C but I decided to give this feature the axe until it has a better infrastructure for optimizations and codegen. The idea is to use it to drive an LED matrix and have a simple web UI to develop "fragment shaders" in Forth. It's developed as part of the Lwan project, although currently it generates GIF files on the fly rather than drive a LED matrix. The source code is here for those that want to play with it: https://github.com/lpereira/lwan/tree/master/src/samples/forthsalon https://github.com/lpereira/lwan/tree/master/src/samples/for...
- firesteelrain 1y agoCould you use the Crafting Interpreters book as a guide to build a Forth interpreter? https://craftinginterpreters.com/ https://craftinginterpreters.com/
- ofalkaed 1y agoSure, but all you really need is the Forth spec and some basic programming skills in any language, even Bash[0] will work. Get a working Forth up and running with the core words and then start improving it and expanding it, develop that basic core into a higher level interpreted language. You will learn far more than you would following a guide. [0] One of my odder Forths was fs4th, FileSystemForth. It was implemented with Bash aliases and variables, the stacks were just paths, there was a directory called stack and in it there were numbered directories to represent the stack and what you pushed and popped onto the stack were files. It was a weird way to work with files but I could see it having niche uses. I also made a more normal Forth in Bash as a quick project to refresh my Bash skills.
- anthk 1y agoWith AWK an RPN calculator it's dumb easy; and from that, a Forth it's just a matter of time.
- stevekemp 1y agoYou can start way simpler than that, as forth won't need an AST or a complex parser. See this repository for a tutorial-approach to building a minimal forth-like language, in golang: https://github.com/skx/foth https://github.com/skx/foth
- vermilingua 1y agoIs writing a Forth in a high-level language feasible/useful as a learning exercise? Almost all the Forth discussion I’ve seen has been about implementing in assembly.
- rangerelf 1y agoNot just a learning exercise, it's also very useful, it allows you to expose application internals in a controlled way so that they can be scripted without exposing your entire codebase to untrusted code.
- deleted 1y ago[deleted]
- timonoko 1y agoProblem with Forth and Lisp is that you can freely start to improve language, so it sort of destroys the usefulness in a company of full of Salarymen and Office Droids. Here is Grok's scheme to make Forth more readable: Below is a Forth implementation of a simple parser that transforms (. (+ 1 2)) into 1 2 + . and executes it. This assumes the input is provided as a string or entered interactively, and the parser outputs the transformed Forth code to the input stream for execution. \ Buffer to store transformed output 20 CONSTANT BUF-SIZE CREATE BUF BUF-SIZE ALLOT VARIABLE BUF-PTR : INIT-BUF ( -- ) BUF BUF-SIZE ERASE 0 BUF-PTR ! ; : ADD-TO-BUF ( c -- ) BUF-PTR @ BUF-SIZE < IF BUF BUF-PTR @ + C! 1 BUF-PTR +! ELSE ." Buffer overflow" ABORT THEN ; : PARSE-EXPR ( addr u -- ) INIT-BUF BEGIN DUP 0> WHILE OVER C@ CASE '(' OF DROP 1 /STRING ENDCASE \ Skip ( ')' OF DROP 1 /STRING ENDCASE \ Skip ) '.' OF BL ADD-TO-BUF '.' ADD-TO-BUF BL ADD-TO-BUF DROP 1 /STRING ENDCASE '+' OF BL ADD-TO-BUF '+' ADD-TO-BUF BL ADD-TO-BUF DROP 1 /STRING ENDCASE BL OF BL ADD-TO-BUF DROP 1 /STRING ENDCASE \ Handle spaces ELSE DUP ADD-TO-BUF \ Copy number characters DROP 1 /STRING ENDCASE REPEAT 2DROP ; : EVAL-EXPR ( addr u -- ) PARSE-EXPR BUF BUF-PTR @ EVALUATE ; \ Execute the transformed string \ Example usage : TEST ( -- ) S" (. (+ 1 2))" EVAL-EXPR ; TEST
- timonoko 1y agoBloody Grok does not seem tobe able to make generic parser, so that Forth whitespace requirements can be ignored. When said I want "Pascal-type" tokenizer, it started to print Pascal-language compiler, which was amazingly complex and long. I cannot imagine many humans can produce that much Forth and survive.
- rep_lodsb 1y agoDid you actually test that code? Never mind the tiny buffer size or possible hallucinations, but just from looking at the "parser", it does seem to do nothing more than copy the expected tokens in the same order it reads them on input, ignoring any parentheses. This CAN'T possibly work, not even for the example input. Maybe if it reversed the string, but unless I've forgotten completely how to read Forth it doesn't do even that. And for some reason this kind of garbage is apparently the future of programming, and people seem compelled to post it everywhere...
- spelc 1y agoI have made a living with Forth since 1981. I have written very few new Forth kernels, but have ported them to many CPU architectures. Forth is no longer fashionable, but it works. Writing a new Forth may be an interesting project, but you will not write a good one until you have a few applications under your belt. Forth is a very subtle language. The internet is full of abandoned Forth kernel projects. Many of these may well have succeeded if they had a purpose that was not yet satisfied. It is perfectly possible to write large applications in Forth. One of our clients has an application of 1.4 million lines of Forth source code. It is hosted on VFX Forth, which compiles Forth to native code. The VFX version runs at least ten times faster than the previous threaded code version, built on MPE's ProForth compiler.
- jcalvinowens 1y agoI'm really curious, how many people collaborate on that 1.4MLOC application? I'd think you might end up with "too many cooks in the kitchen" sort of problems because of how much power the language gives everybody. It's a sort of silly trivial example... but I've watched multiple python codebases slowly become unmaintainable because Python has no way to enforce what can be called or overridden by instances or subclasses, and the org grew beyond its ability to enforce discipline about it in code review. I feel like Forth would offer orders of magnitude more "temptations" for somebody who just needs to get something done, but I've never seen it done at scale.
- vaxman 1y agoThe guy who invented NodeJS (a "fashionable" giant hack) got up and said he regretted it and now uses Go. Did that stop anyone from running up the tech debt with NodeJS? After all, "everyone is doing it!" So easy to start banging out JavaScript... At the other extreme you have Swift which is not Good (as opposed to no good) until V5 which is now obsoleted by V6. Do we all know how screwed Apple is when it comes to LLM training coverage of Swift 5 or 6? Short of their own codebase (and even then), where are they going to get enough training data to help all the Vibe Coders and novices? Could take years to resolve this. Weird future, eh? So FORTH may not be "fashionable" but it is hella fun and very good in a number of dimensions. With the right LLM interfaces (which love large libraries of concise word-like primitives), world domination is assured.
- blippage 1y agoI've messed around with Forth before, and even created my own one. I could do cool things with it, and gotten a DSL-like language. I used words like IMMEDIATE and POSTPONE to create words that create words. I quickly get lost doing that, though. Forth is very very cool to play with. Pragmatically, virtually any other language is better.
- norir 1y agoIt's a bit of a shame that Forth has become almost synonymous with indirect threading implemented in assembly. This is true and somewhat interesting of most implementations, but obfuscates the deeper philosophy of Forth. For me, this is about self-reliance and building your own tools that are suited for the specific task at hand. I don't care for a lot of the standard forth-isms, such as the way branching and recursion are handled. But I make extensive use of the threaded execution model. One quickly notices that anything that can be pipelined naturally fits into this model. Programming in forth style is often similar to programming with unix pipes except you aren't restricted to string input and output and you do not need to fork subprocesses (though you certainly can!). Learning how to program in forth, which as so many have pointed out is best done by implementing your own, will fundamentally change the way you program in all languages. Chaining method calls in data pipelining is somewhat forth-like. As far as implementing a forth, the main interpreter can be written in 14 lines of lua (using lua varargs as the parameter stack): local thread thread = function(first, next, ...) if first then if next then local cont = thread(next, ...) return function(...) return cont(first(...)) end else return first end else error "require at least one task" end end Here is how to use it: local push = function(value) return function(...) return value, ... end end local print = function(msg, ...) print(msg) return ... end local hello_world = thread(push "Hello, world!", print) hello_world() And for fun, here is dup: local dup = function(top, ...) return top, top, ... end The value of these constructs may not be immediately obvious, but I have built a nice compiler in lua on top of it. (I copied the thread definition above directly from my compiler source code).
- tabemann 1y agoI went a completely different route when implementing zeptoforth (https://github.com/tabemann/zeptoforth https://github.com/tabemann/zeptoforth) -- I went right for implementing a fully-featured system rather than focusing on minimalism, and came out with something that I am extremely comfortable writing non-trivial code with rather than a toy that shows how small of an implementation I can make without regard to being a practical tool. And yes, zeptoforth is very big as Forths (especially microcontroller Forths) go -- because the goal is to make something that can be used to program real systems out of the box with minimal effort on the user's part.
- vaxman 1y agoHave you had any feedback from The Chuck? How about a ZeptoFORTH-MCP bridge (to implement services in ZeptoFORTH that can be called by local or remote agents) and an official way of programmatically configuring and interacting with an LLM, like on a Jetson, from ZeptoFORTH?
- tabemann 1y agoI have not had any feedback from Chuck, but from looking at Chuck's work I think he wouldn't consider zeptoforth to be all that Forthy. As for a zeptoforth-MCP bridge, well, I am not really a fan of LLM's. At work we have GitHub Copilot, and I find its autocomplete to be more of a nuisance than anything, as I never like its suggestions and find the fact that it suggests anything, which I then have to reject, as largely something that interferes with my flow.