sci

https://github.com/babashka/SCI - also see #babashka and #nbb
kwrooijen 2020-08-06T19:37:24.404800Z

@borkdude You are truly haunting me haha https://clojure.atlassian.net/browse/CLJ-2568

kwrooijen 2020-08-06T19:37:33.405Z

Just bumped into this issue

borkdude 2020-08-06T19:48:57.405400Z

Yeah, that's pretty annoying, but please do try out my patch :)

kwrooijen 2020-08-06T19:50:22.405600Z

Trying it out, seems to work

borkdude 2020-08-06T19:50:38.406200Z

@kevin.van.rooijen Maybe some activity in that ticket will bring it more to the attention of the core team? ;)

kwrooijen 2020-08-06T19:51:07.406400Z

I'll give it a poke haha

borkdude 2020-08-06T19:52:55.407400Z

I believe you can also vote, if you have access to JIRA

kwrooijen 2020-08-06T19:53:29.407700Z

I was using it for macroexpand-all which didn't preserve the meta. But now I have a completely different issue and a bit of a chicken and egg situation regarding macros lol

kwrooijen 2020-08-06T19:53:57.408300Z

I'm honestly not familiar with Core clojure contribution, I'll go take a look for that vote button

kwrooijen 2020-08-06T20:01:22.409700Z

Ok need to follow a bit of a process to get everything set up. I'll do that later I think

borkdude 2020-08-06T20:01:55.410600Z

OK. Meanwile I added a comment as a reaction to someone else

kwrooijen 2020-08-06T20:02:12.410900Z

@borkdude Would it be possible for edamame to evaluate (specific) macros at parse time? And adding the metadata after expansion?

borkdude 2020-08-06T20:03:04.411800Z

Maybe you can already accomplish this with :postprocess?

kwrooijen 2020-08-06T20:03:24.412200Z

I'm writing a Clojure transpiler, and if I can expand -> at parse time, I won't have to actually implement those types of macros myself

kwrooijen 2020-08-06T20:04:24.413100Z

Hmm actually maybe I could

kwrooijen 2020-08-06T20:05:40.414700Z

I'd have to somehow add the metadata back to the new form, that's the main problem I have. One quick hack would be to turn the expanded form into a string, parse it again with edamame, then adding up the metadata

borkdude 2020-08-06T20:07:46.415100Z

@kevin.van.rooijen

user=> (e/parse-string "[(twice x)]" {:postprocess (fn [{:keys [:obj :loc]}] (if (and (list? obj) (= 'twice (first obj))) `(do ~@(rest obj) ~@(rest obj)) obj))})
[(do x x)]

borkdude 2020-08-06T20:08:41.415500Z

adding to the new form: with-meta and vary-meta?

kwrooijen 2020-08-06T20:10:33.416100Z

Interesting, edamame parses inside out?

borkdude 2020-08-06T20:11:31.416600Z

not really, but postprocess will hit the inner elements first, since they are parsed while the outer object is parsed

kwrooijen 2020-08-06T20:14:28.417800Z

The only problem with this is that I won't have any metadata for the inner element (the do and the two xs). The x will have meta data if you use vary-meta, but it will be "outdated" since it's pre-expansion

borkdude 2020-08-06T20:15:48.419300Z

I see yeah, maybe postwalk the expression with the same location metadata

borkdude 2020-08-06T20:16:29.420100Z

The only other option would be to postwalk the entire AST afterwards. This is very common in Lisps: a clear separation between the reader and the evaluator

borkdude 2020-08-06T20:17:11.420300Z

which is also what babashka and sci do

kwrooijen 2020-08-06T20:20:08.420800Z

Postwalk the entire ast while holding on to the old metadata you mean?

borkdude 2020-08-06T20:20:42.421300Z

yeah, or maybe not postwalk, but just something that recursively walks over the AST

borkdude 2020-08-06T20:20:53.421600Z

you can take a look at sci for how this is done

borkdude 2020-08-06T20:21:30.422200Z

maybe you can even use sci for the transpilation btw

borkdude 2020-08-06T20:21:46.422500Z

but also maybe not, depends

kwrooijen 2020-08-06T20:22:14.423100Z

Not sure how sci would help. I'm currently just generating strings mostly

kwrooijen 2020-08-06T20:22:37.423300Z

But maybe I'm missing an important detail

kwrooijen 2020-08-06T20:24:19.424100Z

I could use sci to evaluate things. But I'd still need to preserve the relationship between clojure code and the generated output

borkdude 2020-08-06T20:31:07.424400Z

In sci you can override clojure.core functions and macros to do what you want, e.g. generate strings

kwrooijen 2020-08-06T20:35:53.425400Z

Yeah, I'm not doing any much different right now. Basically traversing the datastructure and matching on types / specific keywords

kwrooijen 2020-08-06T20:36:17.425900Z

While holding on to the context of the current line / defined functions / variables

borkdude 2020-08-06T21:11:33.426400Z

@kevin.van.rooijen

(require '[sci.core :as sci]
         '[clojure.string :as str])


(println
 (sci/eval-string "(println (/ 1 2 (+ 1 2 3)))"
                  {:bindings
                   {'println (fn [& args]
                               (format "console.log(%s);"
                                       (str/join ", " args)))
                    '/ (fn [& args]
                         (format "(%s)"
                                 (str/join " / " args)))
                    '+ (fn [& args]
                         (format "(%s)"
                                 (str/join " + " args)))}}))

;; $ clojure /tmp/transpile.clj | node
;; 0.08333333333333333

kwrooijen 2020-08-06T21:14:41.427700Z

I don't think you're able to keep track of how deep you're going into the datastructure using this method, or could you? I'm transpiling to GDScript, which is based on Python sigh. So I need to keep track of the indentation

kwrooijen 2020-08-06T21:14:57.427900Z

Very painful

borkdude 2020-08-06T21:16:48.428400Z

don't know

borkdude 2020-08-06T21:18:05.429200Z

I assume there is a 1 to 1 translation of nested sexprs to indented python forms, but you probably have more experience with that syntax than I do

borkdude 2020-08-06T21:19:37.429500Z

also I don't think you can override special forms in sci (right now)

borkdude 2020-08-06T21:20:18.430100Z

well maybe you can, except do probably. also not def. it's just never been needed so far ;)

kwrooijen 2020-08-06T21:22:20.432Z

All the & body values in defn would have to be iterated over, with 1 indentation extra. Then anything in any of those forms, you'd have to indent as well

kwrooijen 2020-08-06T21:23:11.433300Z

But I think at that point you're not using the bindings anymore, unless the body values are evaluated before defn (I guess that would be correct?)

kwrooijen 2020-08-06T21:23:29.433800Z

Then again, they wouldn't have a context of how deep they are

borkdude 2020-08-06T21:24:22.434200Z

user=> (sci/eval-string "(defn 1 2 3)" {:bindings {'defn (fn [& xs] (prn xs))}})
(1 2 3)
nil

borkdude 2020-08-06T21:24:36.434500Z

it depends if your binding is a function or a macro

kwrooijen 2020-08-06T21:26:05.434800Z

Yeah but at this point, why not just use multimethods?

borkdude 2020-08-06T21:26:19.435200Z

user=> (sci/eval-string "(defn 1 (+ 1 2 3) 3)" {:bindings {'defn ^:sci/macro (fn [&form &env & xs] (prn :form &form) (prn xs))}})
:form (defn 1 (+ 1 2 3) 3)
(1 (+ 1 2 3) 3)
nil

borkdude 2020-08-06T21:26:27.435500Z

multimethods are functions

borkdude 2020-08-06T21:26:50.435800Z

so by definition, their arguments are evaluated first

borkdude 2020-08-06T21:27:13.436Z

anyway, I'm afk now :)

kwrooijen 2020-08-06T21:27:31.436200Z

All right, thanks for the input!

kwrooijen 2020-08-06T21:35:37.437400Z

Sadly this won't work either because I want to be able to display compilation errors. And using the :bindings method means I wouldn't know where these definitions are (I think).

borkdude 2020-08-06T22:03:18.438200Z

you know if you make these bindings macros, so you can look at the form (the first arg of the macro)

borkdude 2020-08-06T22:03:25.438400Z

and this form contains metadata

borkdude 2020-08-06T22:03:38.438600Z

see this one: https://clojurians.slack.com/archives/C015LCR9MHD/p1596749179435200

kwrooijen 2020-08-06T22:12:49.438900Z

Oh that's really cool

kwrooijen 2020-08-06T22:22:59.441Z

Does sci have something like a default binding? I don't see it in the options at least. Since if the user inputs an unknown function (foo 1 2 3) it'll need the default treatment, which would be "foo(1, 2, 3)"

kwrooijen 2020-08-06T22:23:37.441500Z

It's getting a it late. I'm probably going to head off. Thanks again!