clojure 中宿主互操作点宏是如何实现的

How is the host interop dot macro implemented in clojure

我正在学习 clojure 并遇到了这种与主机平台互操作的语法。

(.toUpperCase "fred")
-> "FRED"
(.getName String)
-> "java.lang.String"

我很好奇这是如何实现的。文档说它们在扩展时扩展为点特殊形式

(.instanceMember instance args*) ==> (. instance instanceMember args*)

这里似乎发生了特殊的行为。通常宏调用中的第一个参数只是宏符号。然而,在这种情况下,(. 在某种程度上是一个通用宏,它根据 (. 之后的调用中的第一个参数扩展为不同的东西,例如 (.toUpperCase ...(.getName ...

那么这是如何实现的呢?实现宏是否使用了一些我不知道的特殊语法来获得该行为,或者该类型的宏实现行为在用户空间中不可用。它使用实际的宏还是 reader 宏?

这些在手册的 Java Interop 部分的 Member Accress 下进行了描述 这些是在宏扩展时扩展的特殊形式。

The instanceField form is preferred for fields and required if both a field and a 0-argument method of the same name exist. They all expand into calls to the dot operator (described below) at macroexpansion time. The expansions are as follows:

(.instanceMember instance args*)  ==> (. instance instanceMember args*)
(.instanceMember Classname args*) ==> (. (identity Classname) instanceMember args*)
(.-instanceField instance)        ==> (. instance -instanceField)
(Classname/staticMethod args*)    ==> (. Classname staticMethod args*)
Classname/staticField             ==> (. Classname staticField)

Clojure 中有几种形式的 maco:

  1. 正常类型或 "macros"。你可以写这些。
  2. reader macros, which you can extend some of using tagged literals
  3. 这些带有宏展开时间求值的特殊形式。另见 the expansion for the new special form

我不能轻易回答 Clojure,但它在 ClojureScript 中是这样工作的:在编译器中确实是作为一种特殊情况处理的。在表单上尝试 "normal" 宏扩展后,它会返回到此处的一些逻辑,该逻辑查找以点开头的符号(并且当宏扩展使用 new 时,这也类似地处理符号末尾的点):

https://github.com/clojure/clojurescript/blob/b38ded99dc0967a48824d55ea644bee86b4eae5b/src/main/clojure/cljs/analyzer.cljc#L3882-L3899