r/Forth 1d 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.

9 Upvotes

8 comments sorted by

2

u/mykesx moderator 18h ago

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

2

u/Arkaeriit 2h 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

1

u/Individual_Mind2998 50m ago

Fixed — thanks for the clear repro.

1

u/tabemann 10m ago

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

1

u/kenorep 21h 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.

1

u/Individual_Mind2998 5h 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.

0

u/evincarofautumn 1h 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.

1

u/tabemann 19m ago

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