technoblogy / ulisp-esp

A version of the Lisp programming language for ESP32-based boards.
MIT License
110 stars 37 forks source link

hot-swap reader source mid-read #58

Closed dragoncoder047 closed 1 year ago

dragoncoder047 commented 1 year ago

Currently (read s) reads an entire form from the stream s before it returns, and if it gets an EOF mid-form it complains. Could a read-seq function be modified to take multiple streams (i.e. (read-seq s1 s2 s3 ...) any of which could be strings or streams, and go to the next when it hits an EOF on one stream?

The reason why I am asking is so I can implement some sort of module loading behavior. The LispLibrary has require but it is very restrictive and you have to remember to require each function in the LispLibrary that another function in the LispLibrary depends on.

You could construct a lambda wrapper on the fly to make pseudo-modules. Something like this:

(defun import (file bindings)
  (with-sd-card (fs file)
    (eval (read-seq "((lambda nil " fs (format nil "(list ~s)))" bindings)))))

Of course this would require changing defun to push the definitions to the local env, not always the global env, so that you can have local nested functions defined as if they are global (this change is super easy, one line) without having to resort to using/implementing something like labels.

This would allow people to create modules using closures to allow functions to talk to each other without explicitly importing them.

Long story short, the whole need for this would be to reduce RAM usage in the reader because otherwise you would have to use read-from-string, a loop, and on-the-fly code construction (another push for macros and quasiquoting!!).

technoblogy commented 1 year ago

Sorry for the delay in replying - still thinking about this.

dragoncoder047 commented 1 year ago

I will admit this patch would make Lisp behave more like Python, and that may not be desirable if the goal is to adhere to Common Lisp and not Hy. You could just list it as a patch that people could apply if they wanted.

dragoncoder047 commented 1 year ago

Actually, I don't think this is necessary.

You could (read) one form, don't eval it, and if it starts with defvar or defun change it into a lambda form if needed and place it into the declarations block of a let* form, and then try the next form, etc. until all are read, then eval the toplevel let* that was constructed.