babashka / sci

Configurable Clojure/Script interpreter suitable for scripting and Clojure DSLs
Eclipse Public License 1.0
1.21k stars 85 forks source link

support macros? #59

Closed borkdude closed 4 years ago

borkdude commented 5 years ago

We might get an idea how to implement the templating from looking at:

dundalek commented 4 years ago

Having macros would be sweet.

I am experimenting with using sci for closh, so that it could be graalified to a binary with fast startup. But closh heavily depends on macros. I was surprised that when I expand the macros before passing them to sci I was able to get basically all of the tests passing. This is very exciting, you did a really great work on sci :clap:

borkdude commented 4 years ago

@dundalek Thanks! Yeah, that would indeed be exciting. I'll look into macros sometime soon.

borkdude commented 4 years ago

@dundalek First test is passing: https://github.com/borkdude/sci/blob/b0d05444da98f9026bf519c6f6f08a1ca14ff486/test/sci/core_test.cljc#L383

No templating implemented yet, but (defmacro foo [x] (list 'do x x)) (foo (prn :dude)) should already work. To be continued.

borkdude commented 4 years ago

@dundalek Now this also works (in the defmacro branch):

(defmacro foo [x] (let [y (str x x)] `[~y ~y]))

Can you give me a couple of macros for testing? Since sci doesn't really have user-defined namespaces yet, I'm not resolving symbols to their namespace yet in the templating. I wonder if this is needed for your use case.

borkdude commented 4 years ago

Unquote-splicing also works now:

$ lein run '(defmacro foo [x] `(list ~@[x 2 3])) (foo 1)'
(1 2 3)
dundalek commented 4 years ago

@borkdude very cool progress

One thing I discovered is that arguments to macros are being evaluated, so example cases like these:

 (is (= "bar"
        (eval* "(defmacro foo [x] (str x)) (foo bar)")))

 (is (= :bar
        (eval* "(defmacro foo [x] `(keyword ~(str x)) (foo bar)")))

Give error like:

Execution error (ExceptionInfo) at sci.impl.utils/throw-error-with-location (utils.cljc:51).
Could not resolve symbol: bar [at line 1, column 33]
borkdude commented 4 years ago

Thanks. Will notify you when it's fixed.

borkdude commented 4 years ago

Should be fixed now.

borkdude commented 4 years ago

I should note that it's also possible to bring in predefined macros via metadata:

https://github.com/borkdude/babashka/blob/master/src/babashka/impl/clojure/core.clj#L16

Not sure if that is relevant to your use case, or if you want users to be able to define macros. This feature is not yet documented and may change in the future.

dundalek commented 4 years ago

@borkdude I was able to figure out the last remaining issue. The thing was I had a self-referencing macro inside backtick, so it referenced fully qualified variant of itself. I was able to make it work with workaround by adding both unqualified and fully qualified variant to bindings.

So for my use cases this now works :100:.

borkdude commented 4 years ago

@dundalek Would it be better if sci fixed something in this regard, if so could you post a repro? Or is this ready for merge? Do you need a release for closh or do you refer by commit sha?

dundalek commented 4 years ago

It is good as is, makes sense to me to merge it in. I use sha in deps.edn so no need to cut a release just for me.

Next step for me is to try to get the whole thing compile with graal into a native image :)

borkdude commented 4 years ago

@dundalek OK, I merged and deleted the branch. Please refer to a commit on master from here after.

Do I understand correctly you're not using the user defined macro feature but just pass bindings with :sci/macro?

If you need any help with GraalVM, just let me know. As a heads up: macroexpand and resolve probably don't work on 1.10.1 without any workarounds but it might on 1.9.0 (YMMV). But there are workarounds. One thing that will definitely fail is eval.