This is too much to bear and I had to create an account for this!
Might I interest you in the opposite? Sort of a Lisp implemented in Forth that can compile Lisp functions to native code, all of this in less than 500 lines of Dusk code.
Condolences on finally opening an HN account. I greatly appreciate your work even though I disagree with much of it. DuskOS in particular seems excellent.
> What's the most interesting program you've written in comp/lisp?
None. I wrote comp/lisp because my lack of Lisp knowledge seemed to me like a liability as I invest more and more time in Forth. I had nothing I felt like writing in Lisp, so I thought it would be fun to implement one.
While I can't call myself a Lisp programmer, I think I can say now I have an idea of what it's about. It didn't grow on me, so I leave it aside for now. Maybe I'll come back to it later.
> I infer that you are just using dynamic scoping, thus ruling out both upward funargs and tail-call elimination?
Tail-call elimination is possible with my implementation within the same function, but it's not "natural". I'm not sure of what it would take to have it like we see them in other lisps, or if it's possible at all. It's been a while since I swam in that pond, details aren't fresh in my mind.
As for scoping, I'm unfamiliar with the theory. I don't think it's what we call "lexical scoping" because variable names have very little meaning in the compilation process. It's eliminated very early and only "compile time Parameter Stack offset" is kept in the AST.
I don't know what you mean by "upward funargs", but if it's to access arguments from outer scope, yes it's possible. That's exactly the point of the last link you pointed to: lambda generation. A call to a closure generates a lambda which has its outer scope "injected" to PS.
"Upward funargs" is where you return a closure which captures the bindings of your local variables, so it can access arguments from an outer scope which no longer exists. Like this:
This makes Scheme closures a fully general substitute for Smalltalk-like objects.
Upward funargs generally require a non-stack-like allocation discipline for the local-variable bindings.
While implicit variable capture like Scheme is the most common way to do closures, it isn't the only way; C++ and old versions of Python (before nested_scopes) have a form of closures where you instead explicitly list the variables you want to capture. Old dynamically-scoped Lisps like Lisp 1.5 had a different solution which I don't understand; see AI memo AIM-199, "The function of FUNCTION in LISP".
Usually dynamic scoping is incompatible with tail-call elimination (except in the special case of tail recursion) because you can't eliminate a tail call if which you have more work to do after it returns; usually popping some variable bindings off a context stack amounts to "more work to do". Maybe your parameter stack has a way for that to happen implicitly, for example by implicitly restoring a parameter-stack offset on function return.
Yeah, to be honest I'm too out of this context to speak of deep technicalities, but a quick test seems to indicate that Dusk's comp/lisp can do something similar to your first example, unless I misunderstood it:
$ make run
stty -icanon -echo min 0; ./dusk ; stty icanon echo
Dusk OS
79KB used 31MB free ok
needs comp/lisp
ok
lisp (defun make-adder (x) (lambda (y) (+ x y)))
ok
lisp (defun x+3 (x) ((make-adder 3) x))
ok
lisp (defun x+7 (x) ((make-adder 7) x))
ok
lisp. (x+3 7)
10 ok
lisp. (x+7 8)
15 ok
That could work without upward funargs because, in your version, (make-adder 3) and (make-adder 7) aren't live at the same time, so they might be using the same "x" instead of two separate "x" bindings. My hunch is that that "x" is in memory space that would be overwritten if you called another largish function in between calling make-adder and invoking its return value, but I haven't grokked comp/lisp fully enough to be sure that that's true.
Thanks for the excellent work you're doing! It's (extremely) high on my list of things-to-do if I ever get more time for learning and progressing my very rudimentary programming knowledge.
This is excellent as well. I don't really jive with the Collapsnik aspect but I absolutely love powerful systems that can run in constrained environments.
Thanks for sharing, I'll be diving into this for a while
I came in to this thread to ask if anyone had done this, given an intuition that it seems like a fairly natural path to bootstrapping lisp on metal. Didn't even have to ask!
Might I interest you in the opposite? Sort of a Lisp implemented in Forth that can compile Lisp functions to native code, all of this in less than 500 lines of Dusk code.
https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/doc/co... http://duskos.org/