4 ms·
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 Li
by vdupras 2y ago
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.
https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/doc/comp/lisp.txt https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/doc/co...
http://duskos.org/ http://duskos.org/
- sourcepluck 2y agoThanks 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.
- wild_egg 2y agoThis 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
- kragen 2y agoCondolences on finally opening an HN account. I greatly appreciate your work even though I disagree with much of it. DuskOS in particular seems excellent. I hadn't looked at comp/lisp before. It definitely looks like a real Lisp to me. In https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/mem/cons.fs#L19 https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/mem/co... I see that you have dynamic typing, implemented in assembly, and from https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/mem/cons.fs#L25 https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/mem/co... to line 70 you have a garbage collector. From https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/comp/lisp/compile.fs#L100 https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/comp/l... I see you have closures, but I'm not sure how memory allocation for their captured variables works; from https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/comp/lisp/env.fs https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/comp/l... I infer that you are just using dynamic scoping, thus ruling out both upward funargs and tail-call elimination? What's the most interesting program you've written in comp/lisp?
- vdupras 2y ago> 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.
- kragen 2y ago"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: scheme@(guile-user)> (define (make-adder x) (lambda (y) (+ x y))) scheme@(guile-user)> (define x+3 (make-adder 3)) scheme@(guile-user)> (define x+4 (make-adder 4)) scheme@(guile-user)> (x+3 7) $1 = 10 scheme@(guile-user)> (x+4 8) $2 = 12 In Scheme those bindings are even mutable: scheme@(guile-user)> (define (counter n) (lambda () (set! n (1+ n)) n)) scheme@(guile-user)> (define booga (counter 53)) scheme@(guile-user)> (booga) $3 = 54 scheme@(guile-user)> (booga) $4 = 55 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. I hope this helps. Keep being awesome!
- wwweston 2y agoI 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!