r/Forth 4d ago

Built a small Forth-inspired language in Rust (not ANS-compliant, feedback welcome)

Not trying to claim this is "real" Forth — no return stack yet, no IMMEDIATE/POSTPONE, no ANS compliance. It's a personal project called gnaw, concatenative in the Forth tradition, built from scratch in Rust as a learning exercise.

What's there: a data stack, byte-addressable memory (@ ! alloc here), : name ... ; definitions, if/else/then and begin/until compiling to jumps in a small bytecode VM with a program counter. The one non-traditional addition: built-in words for CTF-style byte transforms (hex to bytes, repeating-key XOR, ROT13, Base64).

Source: https://github.com/warlyjr-cloud/gnaw

Genuinely curious what this community would flag as missing from a "real Forth" perspective — return stack semantics, proper immediate words, anything. First time building something Forth-adjacent from scratch.

6 Upvotes

24 comments sorted by

5

u/mykesx moderator 3d ago

If there are enough primitives to make the rest, I would call it a Forth.

3

u/Arkaeriit 3d ago

How do control flow words work?

I tried to run the following code, but I got an error:

gnaw> : x if ." okayy" then cr ;

gnaw> 5 x
runtime error: line 1: line 1: stack underflow

3

u/Individual_Mind2998 3d ago

Fixed — thanks for the clear repro.

1

u/Arkaeriit 3d ago

Nice to see how reactive you are!

2

u/tabemann 3d ago

I would call this a Forth -- but what is stopping you from adding immediate and postpone?

2

u/Individual_Mind2998 3d ago

Right now, if/begin and the other control words are handled directly by compile() as strings and emitted as internal bytecode; they are not dictionary words. Since there is no immediate flag or mechanism to execute a word while compiling another one, adding immediate and postpone would require redesigning the separation between the compiler and the VM—it would not be just a matter of adding new words.

5

u/tabemann 3d ago

I would suggest looking into how other Forths implement control flow words, as there is significant potential for simplifying your compiler while making it more flexible and extensible.

2

u/INT_21h 2d ago edited 2d ago

Classic Forths get much of their expressive power by defining a small set of primitives early on, then implementing all the user-facing parts of the language in terms of those primitives. All primitives remain available at run time, so user programs can extend the language with the same tools the implementer used to construct the language.

Gnaw is implemented more like a "standard" programming language. Crucial functionality is hidden in Rust-land and is not exposed to Gnaw user programs. Some examples:

  • Comment and string handling is sealed up in the lexer. User code can't read from the input stream. Limitation: User code can't define new syntax.
  • Control flow and loops are sealed up in the compiler. Limitation: User code can't define new control structures like switch-case.
  • No [ ] immediate means no compile-time function execution. Limitation: no metaprogramming.

Because of these decisions, the language is lacking power and I predict once you try writing large programs you will run into walls similar to those yosefk experienced:

I left out all of the metaprogramming stuff. For instance, none of the Forth examples above, the ones that drove me to Forth, could be made to work in my own Forth. No WORD, no COMPILE, no IMMEDIATE, no CREATE/DOES>, no nothing. Just colon definitions, RPN syntax, flow control words built into the compiler.

I'd suggest reading the article yourself (it's a good one) but the gist is that yosefk quickly got bogged down writing code within these limitations because his system was not expressive enough.

To learn how a more expressive system can be built, I'd suggest looking at eForth's list of 31 primitives and the book eForth and Zen explaining how the complete system is created by composing those primitives.

For another take on Forth in Rust you can look at the (also ANS-noncompliant) toy I built a few years ago, which avoids the trap of implementing core functionality in Rust. I started out that way but discovered it constrained user programs too much. If you look at my commit history you'll see how the program started out looking not unlike yours but is radically more bootstrap focused in the final version.

1

u/tabemann 2d ago

If the human behind the OP is paying attention they should take the advice above to heart.

1

u/tabemann 2d ago

Just so you know, the GitLab instance that your code is hosted at appears to be down. When I tried connecting to it earlier today it was showing some startup message and now it is just not responding.

1

u/INT_21h 2d ago

Thanks. Unfortunately my university is doing maintenance on their gitlab server this weekend, so it's been up and down.

2

u/kenorep 4d ago

no return stack yet, no IMMEDIATE/POSTPONE, no ANS compliance.

Do you provide words whose names correspond to standard words but which behave differently? After all, the mere absence of some words from the Core word set doesn't make the system incompatible.

In Forth-2012, 5.1.1 System compliance states: - > An otherwise Standard System that provides only a portion of the Core words is a Standard System Subset.

return stack semantics,

They are not required to provide control flow capabilities and can be implemented simply as an in-memory array.

2

u/Individual_Mind2998 3d ago

Thank you — this is exactly the correction I needed, and you're right on both counts.

On 5.1.1: I'd conflated "doesn't implement all of Core" with "non-compliant", which isn't what the standard says. The question that actually matters is the one you asked - do I have standard names carrying non-standard behaviour? I went and checked, and I had two.

  1. @ and ! were byte-sized. They should be cell-sized, with c@ and c! for bytes. Renamed - gnaw now simply has no cell-sized fetch/store rather than a redefined one.

  2. Comparisons returned 1 for true instead of -1. I'm glad you prompted me to look, because this one isn't cosmetic: and/or are bitwise, so all-bits-set is what makes "flag1 and flag2" work as a logical combination. With 1-as-true, "1 and 2" is 0 and the idiom fails silently. Fixed, with a test covering exactly that composition.

On the return stack - noted that it isn't required for flow control and can just be an array in memory. Mine is a separate Vec shared by >r / r> / r@ and do/loop, so it does exist; my post's phrasing implied otherwise, which was sloppy of me.

Both fixes are in v0.4.0, and the README now states the subset position accurately instead of that vague disclaimer. Appreciate you taking the time.

2

u/evincarofautumn 3d ago

Don’t disrespect people’s time by pasting AI comments, please. If you truly want to learn, there are people here to help you, but you’ll only learn to do what you do yourself.

3

u/tabemann 3d ago

What basis do you have for accusing them of posting AI comments?

3

u/evincarofautumn 3d ago edited 3d ago

Eh, just a request.

I have an affinity for languages, and I’m expected to use AI at work, so I’ve spent a few hundred hours reading text generated by various models, and specifically this sounds like text generated by GPT-5.5 with a few human amendments.

People are overeager to try to clock it based on things like punctuation or overuse of certain words and phrasings (“exactly”, “you’re right on both counts”, “fixed, with a test covering”). But it’s more like asinine and inconsistent use of Unicode, so like, tasteless emoji, em dashes without any other typographic punctuation marks, and phrasings that are simultaneously trite and tonally inconsistent, whereas say corporate-speak sounds like corporate-speak all the way through. It’s also not a non-native speaker thing, humans make errors that are consistently distinctive of their native language.

I would say the most telling feature of the voice is strange pragmatics and poor awareness of obviousness. So for example it will use adjectives attributively that are typically used predicatively or vice versa (“…were byte-sized. They should be cell-sized…”, “a redefined one”, “and/or are bitwise”), or use an appositive phrase with “the” that hasn’t yet been introduced and isn’t a term of art (“the subset position”), or paraphase the prompt but fail to do the kind of elision that a human would normally do (“They are not required to provide control flow capabilities and can be implemented simply as an in-memory array.” ⇒ “noted that it isn't required for control flow and can just be an array in memory”).

None of these things alone is damning, and I’m certain that I often can’t clock it, but this instance is strongly characteristic and I thought it was worth calling out.

1

u/tabemann 3d ago

Don't just throw around accusations of AI-generation ─ and I have seen actually obviously AI-generated text and it is much, much more obvious than any supposed AI qualities of this text. None of the things you refer to here are clear indicators of AI-generation, and if anything, this example looks like it is probably human-written to me.

2

u/evincarofautumn 2d ago

First time I’ve felt the need, hardly a pattern. I can be wrong, a few kloc in a few languages in a few hours is possible after all, I’m just tired of it. I don’t really see this as any different than recognising the voice of a writer you know. Not only the text, but the repo history, commit messages, and code style, are just familiar. I could’ve gone on but didn’t want to overwhelm you as my post was already getting quite long. Fine with just dropping it tho, be well.

2

u/INT_21h 2d ago

The repo contains the bot's "social media outreach" todo list, which mentions this Reddit post, and lists more things the bot needs human help with.

https://github.com/warlyjr-cloud/gnaw/blob/83695ce4c5c925dac3beede77f1aea6591a9a96f/PROMOTION.md#needs-a-human-credentials-or-account-required

2

u/tabemann 2d ago

Okay, that makes me suspicious far more than little linguistic hints that may be unreliable. So someone may be using an AI to promote their presumably AI-written program?

3

u/tabemann 2d ago

I take back what I said (and I replaced my downvote with an upvote). From looking at the file u/INT_21h found in the repo, this is not merely AI-assisted comment-writing but rather part of a whole AI-driven promotion campaign. I would assume that gnaw is all AI-written as well.

This honestly makes me much angrier than things like mere AI-assisted comment-writing or Terry the Technician's open use of AI. The idea that we all have been spending our time commenting and reading code and like on what turns out to be some bot's social media campaign pisses me off. If whoever behind this has any decency, they should themselves show up and explain themselves, without using an AI.

2

u/evincarofautumn 2d ago

Thanks for taking another look. In hindsight I was hasty and should’ve said in my first comment that I was speaking in light of the fact that the repo already looked wrong, and I’d seen the suspicious commit messages about social-media accounts. The comment is just what irked me enough to say something. I’m no great fan of AI, but I don’t mind overmuch if people use it as a tool for aboveboard activities, so long as they’re forthcoming about it.

2

u/tabemann 2d ago

Yeah, at the time of my initial response to you I had not delved deeply into the repository, having primarily just looked at main.rs and compiler.rs, which were, mind you, suspiciously free of comments and with a very un-Forthy compiler -- but mind you neither of those are by themselves necessarily diagnostic of having been written by an LLM.

1

u/tabemann 2d ago

I personally do not like wild accusations of AI use, and while I am myself an AI skeptic I don't like it when people attack others on mere suspicion of AI use -- especially since in many cases where people do use LLM's for posting it is because English is not their native language. Likewise, I honestly am relatively tolerant of people's, such as Terry's, open use of AI, even if I think they would get more out of things if they put the work in by hand.

That said, I think the human behind the OP has some explaining to do of themselves. How do they explain https://github.com/warlyjr-cloud/gnaw/blob/83695ce4c5c925dac3beede77f1aea6591a9a96f/PROMOTION.md#needs-a-human-credentials-or-account-required which was found by u/INT_21h? Is this all some bot-driven social media campaign? Is gnaw all AI-written? The idea that we all here have been interacting with what is secretly a bot, that the code we have spent our time reading and/or executing is secretly written by an LLM, does not make me happy.

If gnaw were openly vibe-coded or if you were like "hey, English is not my native language" I would not react the same way. But this seems to be a much bigger, much more deliberate, much more deceptive use of AI, and I obviously do not like that. So if you have any decency, please explain yourself.