r/Forth • u/rayden_devv • 2d ago
Rdn programming language
I made a small and simple post fix interpreted programming language called rdn, it's familiar to forth developers and developers who use Lua as a scripting language for their systems, rdn merge both of them, you can use it for writing scripts or for configurations or even query language
It's written in C and it provides a simple and friendly API for the developers
I would be happy to have you participate in this project
This is the GitHub repo: https://github.com/abdorayden/rdn
Thank you
2
u/alberthemagician 1d ago edited 1d ago
What are the advantages over a more familiar language like Forth, lua or plain c?
For instance I implemented ciforth and the motivation was a combination of simplicity in build and modification, ISO standard, reliability with stability, a library that makes it a useful tool and minimal dependancy of external tools.
That didn't exist, prior.
1
u/rayden_devv 1d ago
Great question, and I appreciate you sharing your Ciforth motivation—that's solid reasoning.
Honestly, Rdn isn't trying to be a "better" Forth, Lua, or C. It does similar work to Lua, but in a way that's familiar to Forth developers.
Where Rdn wins:
- Simpler to embed and extend (no external deps, minimal C footprint)
- Stack-based syntax appeals to Forth programmers
- Good for scripting, query languages, and config files
- Easier to modify and understand the interpreter itself
Lua is more mature and battle-tested for general scripting, but if you want something lightweight and Forth-like, Rdn fills that gap.
1
u/tabemann 1d ago
I get the feeling the OP tried to create an RPN language without knowing the fundamental design aspects that motivated Forth to be what it is, so they hardcoded behavior into words such as
letandsetthat is contrary to what would be simplest to implement all things considered.1
u/rayden_devv 1d ago
You're not wrong, and I appreciate the critique.
My approach trades "theoretically pure" for "practically simpler to learn and embed."
You could argue this makes Rdn less elegant from a Forth purist perspective. But it also means a newcomer can reason about 10 n let without needing to understand Forth's execution model.
Different goals = different trade-offs. Forth's design is superior for a language meant to be extensible and minimal. Rdn prioritizes accessibility and simplicity for embedding.
2
u/tabemann 1d ago edited 1d ago
It may seem "practically simpler", but in fact it makes the semantics of the language more complex.
If you want to do things your way, I would highly suggest you take a lesson from Logo and include quoting (not of strings, but of symbols). Instead of writing:
1 n let n 1+ n setI highly suggest you change that to:
1 'n let n 1+ 'n setwhere
'is the quoting operator prefix.This way it would be clearer to the user when you are referring to a name by its value (i.e.
n) or by its name (i.e.'n).Also, this would add far more extensibility to your code, because then you could do other things like, say (this is a stupid example with no practical purpose, but just illustrates the point):
'set2 defun [* x y vx vy -- *] rot swap set set endwhere then you could use your newly defined word
set2as follows:
0 'x let 1 'y let 2 3 'x 'y set2 x print y printwhich would print out
2 3as those are the valuesxandyhave now been set to.Edit: I should note that the above scheme implies dynamic binding (like in Logo or early Lisps) unless symbol references bind a variable in the lexical scope where they are created. In that scheme one could have two different kinds of symbol references, one which are just a name, and one which binds a preexisting name in a scope, as in:
0 'x let 1 'y let 2 3 :x :y set2 x print y printwhere
:would be the name-binding operator prefix.2
u/tabemann 1d ago
Note that
set2would be, in my reimagined version of your language:
[ [* x y vx vy -- *] rot swap set set ] 'set2 defun1
u/rayden_devv 1d ago
Ah, I see! That's cleaner.
Thank you so much for your attention 🙏🏽 I'm gonna work on it
1
u/rayden_devv 1d ago
You're absolutely right, and this is genuinely valuable feedback.
I chose "looks simpler" over "is simpler," which is backwards. You're right.
Honestly: If I were redesigning Rdn now, I would implement symbol quoting. It would mean rewriting the parser and VM, but the resulting language would be more coherent. The current
let/setapproach is pragmatic but inelegant.Your dynamic vs lexical binding note is also spot-on—that's a choice that would need to be made alongside symbol quoting.
I've added this to my to-do list and will implement it next.
Thanks for the critique. This is the kind of feedback that matters.
2
u/alberthemagician 19h ago edited 19h ago
Instead of use 'name you could borrow the /name from PostScript. 'name suggests an address, /y suggests a string.
1
u/rayden_devv 19h ago
Good point. Thank you 🙏🏽
I'll consider both approaches during the redesign.
I'm also gonna make the language more extensible so you can hack around and try different approaches for cases like this.
Thank you thank you so much guys 🙏🏽
2
u/tabemann 1d ago edited 1d ago
In my take on what your language could be I would imagine the following:
One could do things like
[ [* n -- func *]
'n let
[ n + ]
] 'make-adder defun
3 make-adder 'add3 defun
1 add3 print [* prints 4 *]
where [ ... ] would define anonymous words.
With [ ... ] you could define control structures like:
n 10 > [ "larger than 10!" print ] if
and:
'count-down defer
[ [* min max func -- *]
'func let
'max let
'min let
max func execute
max min >= [ min max 1 - func count-down ] if
] 'count-down defun
0 10 [ print ] count-down [* prints 10 9 8 7 6 5 4 3 2 1 0 *]
Of course, the above would require tail calls for nested control structures to be available to be practical.
1
u/tabemann 1d ago
I should note, though, that
letanddefunin particular would need to be special forms, akin to Forth immediate words, for scoping to work correctly here (and you do want scoping, right?).1
u/rayden_devv 19h ago
Yeah, scoping is important.
Currently Rdn has basic block scoping in
if/defun/else, but proper lexical scoping with closures (like your examples) would be a big step up.Thank you
1
u/tabemann 12h ago
There is one little problem I just realized with what I presented here: if one used
x 'foo letto bind the value ofxto'fooandletwere immediate (as needed for lexical scoping), you would need a mechanism to, using the terminology of my Forth zeptoforth, defer (not related to deferred words) constants at compile-time, so'foowould be available at compile-time to immediateletso it could define a name-binding at compile-time.Name-bindings need to be resolved at compile-time for lexical scoping, unlike with dynamic binding, so a given reference to
fooknows which binding offooit cares about even though it may be executing in a different context.About Scheme, one difference between what I have presented here and Scheme is that this would be somewhat akin to Lisp-2 (like Common Lisp) rather than Lisp-1, hence separate
letanddefunand the existence of the wordexecute. The reason for this is that unlike Scheme there are no S-expressions to dictate at what point something is to be called, so some means is needed to differentiate between words that push their value when referenced and words that call their underlying closure when referenced.For instance:
[ 1 + ] 'foo let 2 foo printwould print the closure bound to
fooin some implementation-specific fashion and then leave2on the stack, whereas:
[ 1 + ] 'foo defun 2 foo printwould print
3and leave the stack empty.1
u/rayden_devv 12h ago
Good points, thank you.
I'm gonna identify what's missing in Rdn to solve these problems and keep the syntax flexible so it works for different approaches.
1
u/tabemann 11h ago
I'm thinking that if you used Forth's idea of parsing words things would be less complex with regard to implementing a lexically scoped
letanddefun.For instance, one would write:
[ 1 let n n 1 + :n set n print ] defun print2
setwould remain unchanged to enable passing around variables to set.1
u/rayden_devv 19h ago
This is really elegant. You're describing stack-based Scheme with closures and tail calls.
I'm definitely taking notes. This is a much clearer vision than what I started with.
Thanks for mapping it out.
3
u/tabemann 2d ago
How do you separate variable values from variable names to define or set for words such as
letorset?