hyperfiddle / rcf

RCF – a REPL-first, async test macro for Clojure/Script
MIT License
278 stars 12 forks source link

Userland macros receives RCF env as &env instead of standard clojure &env value #74

Open ggeoffrey opened 1 year ago

ggeoffrey commented 1 year ago

Reported by @didibus

A minimal repro is welcome

ggeoffrey commented 1 year ago

Minimal repro:



(defmacro my-test-macro [a]
  (prn &env)
  `[~a])

;; No RCF
(let [a 1]
  (my-test-macro a))
;; => {a #object[clojure.lang.Compiler$LocalBinding 0x58d44b9f "clojure.lang.Compiler$LocalBinding@58d44b9f"]}
;; => [1]

;; From inside RCF
(tests
  (let [a 1]
    (my-test-macro a)) := [1])
;; => {a {:op :binding, :name a, :init {:op :const, :env {:locals {}, :namespaces {}, :ns hyperfiddle.rcf-test}, :form 1}, :form a, :local :let, :children [:init]}}
;; => ✅
ggeoffrey commented 1 year ago

It is not clear to me if one can construct a valid instances of LocalBinding. If it is possible then maybe RCF could reconstruct the clojure &env from its own environment.

I tried to produce an instance of LocalBinding, but got an IllegalAccessError failed to access class clojure.lang.Compiler$Expr which has package level visibility.

dustingetz commented 1 year ago
dustingetz commented 1 year ago

clojure.lang.Compiler$Expr clojure.lang.Compiler$LocalBinding analyzeSymbol ... new LocalBindingExpr(b, tag)

didibus commented 1 year ago

I'm using &env to get all current locals, their primitive type, and their attached metadata.

In my macro, I do the following:

(map (fn[[_k v]] (meta (.-sym v))) &env)

To get all local symbols from &env.

I also do this:

(try
    (when-some [java-class (.getJavaClass expr-ast)]
      {:class java-class
       :primitive? (.isPrimitive java-class)})
    (catch Exception _e))

On each LocalBinding returned from &env to get the type of the local and it is a primitive or not.

I believe this is the type for what &env returns: https://www.javadoc.io/doc/org.clojure/clojure/1.11.1/clojure/lang/Compiler.LocalBinding.html

didibus commented 1 year ago

So I saw that riddley macro-expander I think seems to create LocalBindings as it macro-expands: https://github.com/ztellman/riddley/blob/master/src/riddley/compiler.clj#L55 and https://github.com/ztellman/riddley/blob/master/src/riddley/Util.java#L8 which you could also do in Clojure I guess.

It does seem you have to somewhat update the Compiler about it, or the creation of the LocalBinding will throw an IllegalAccessError or something of that sort.

Might be a lead?