一个在Java对象树上创建的Clojure zipper能在zip-filter中使用吗?

4
我目前正在将ANTLR和Clojure结合起来,尝试创建一个Clojure拉链,经过ANTLR返回的抽象语法树。
AST是一套非常Java风格的对象,使用CommonTree对象表示层次结构。
我按照以下方式在CommonTree上创建了拉链:
(defn branch? [tn] (not (zero? (.getChildCount tn))))
(defn children [tn] (.getChildren tn))
(defn make [tn children] (doto (CommonTree. tn)
                           (.addChildren children)))

(defn zip-parse [f] (z/zipper branch? children make (parse f)))

我不确定用这种方式创建CommonTree节点是否有效,因为我还没有进行验证...

我是这样使用这些函数的:

(def zip-ast (parse testfile))

到目前为止,一切都很顺利。这确实有效。我可以使用“向下”,“向右”,“向左”和“向上”功能进行导航。问题出现在我尝试使用zip-filter库定位特定令牌时:
(defn token [loc] (-> loc z/node .getToken .getText))

(defn token= [tokenname]
  (fn [loc]
    (filter #(and (z/branch? %) (= tokenname (token %)))
            (if (zf/auto? loc)
              (zf/children-auto loc)
              (list (zf/auto true loc))))))

(defn java->
  [loc & preds]
  (zf/mapcat-chain loc preds #(cond (string? %) (token= %))))

这是从Chouser的漂亮的xml->函数中拙劣地复制而来。不幸的是,它并不起作用。在zip-filter内部,“auto”函数会向对象添加或删除元数据。除了“纯老Java对象不能有元数据”之外。
我是在错误的方向上努力吗?还是(更有可能的是),我没有足够理解zip-filter就复制它?
1个回答

2
拉链存储分支、子节点,并将函数作为元属性放在包装节点的 loc 上,似乎是 auto 在对象周围的 loc 包装器(向量)上添加或删除元数据,而不是对象本身。所以,我认为这不是问题。
您能否更详细地解释一下“只是不起作用”是什么意思?
有一个看起来可疑的地方(也让我在自定义拉链时出了问题),那就是 `branch?` 函数。请注意,您在 `token=` 中的第一个 `and` 条件检查了 `branch?`——如果标记没有子代,则不会在 `token=` 中匹配,这可能会令人感到惊讶。`branch?` 实际上表示节点是否有子代可能性,因此有时即使它实际上没有子代,您也希望在此处返回 true。除此之外,我没有发现任何明显的问题。
注意:在 Java-> 中,由于您只有一个选项,因此可以简化匿名函数。如果它始终是字符串,则可以将整个匿名函数替换为 `token=`。或者,如果需要处理非字符串情况并返回 nil,则可以使用 `(when (string? %) (token= %))`。
我过去几乎通过不同的方式解决了完全相同的问题。不确定这是否有所帮助,但以防万一...
我构建了一个 Antlr 语法,生成我想要在 Clojure 中作为树遍历和修改的输出。我最终找到的解决方案是:
Antlr 语法 -> Antlr 树语法(按摩、语言无关)-> Antlr 字符串模板(Clojure 特定)生成 -> Clojure 数据结构作为字符串 -> 将 Clojure 数据结构读入嵌套树中(对于我们来说是记录,但可以是任何东西)
其中一个好处是 Antlr Java 代码不直接依赖于 Clojure 代码(仅依赖于传递的字符串格式),这使得 Clojure 代码仅依赖于生成的 Java 代码。这在编译项目时简化了依赖结构(我认为您也已经实现了这个)。另一个好处是语法和树语法是语言无关的,因此可以从相同的语法创建 Clojure 和/或 Java 和/或其他目标(虽然我实际上并没有这样做)。

在token =中第一个条件的捕捉很好。那是我的错误,复制了我没有完全理解的代码。java->函数旨在最终支持其他条件,匿名函数的退化形式是因为我剥离了其他谓词以理解这个问题。总体而言,“只是不起作用”的原因是当auto尝试在CommonTree对象上执行with-meta时,我会收到ClassCastException。with-meta仅适用于IObj的实现者(即Clojure对象)。 - user73774
同时使用Antlr将其“编译”为表示Clojure数据结构的字符串...哇,很酷。我认为。我可以看出这将如何减少Antlr的AST对象和Clojure函数之间的阻抗不匹配。但是,不知怎么的,它仍然感觉像是通过芝加哥从旧金山飞往西雅图。 - user73774
关于错误...嗯。你能发一下堆栈跟踪的相关部分吗?我猜auto上面的调用可能来自children - Alex Miller
抱歉...我是说 children-auto 吗?这是我唯一看到自动调用树节点而不是 locs 的地方。如果我没记错的话,定制 Antlr AST 树节点非常容易。也许你可以创建一个节点类型,它要么是带有元数据的 IObj,要么有一个额外的映射,你可以在其中存储元数据并且修改 zip-filter 来处理它。 - Alex Miller
关于我的路径,我完全同意。这是一条漫长的路。实际上,比我不忙的人需要为Clojure编写一个Antlr后端。这可能不需要太多的代码,我曾考虑过去解决它,但其他需求优先考虑了。 - Alex Miller
1
这是在children-auto中出现的问题。经过更多的反思,看起来我在我的zipper函数中有一个问题。我在我的命名空间中定义了“branch?”和“children”,忽略了zip/branch?和zip/children?函数。我期望我的函数被调用,但是zip中的函数被调用,从loc中解包对象并使用该对象调用我的函数。 - user73774

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接