instaparse如何遍历解析树

How to traverse parse tree from instaparse

我正在试验 Clojure 和 Instaparse。我创建了一种小型玩具语言,但我对如何正确处理生成的树感到困惑。这就是我得到的:

[:ClassDescription 
 [:ClassName "Test"]
 [:Properties 
  [:Property 
   [:PropertyName "ID"] 
   [:PropertyType "Int"]] 
  [:Property 
   [:PropertyName "Name"] 
   [:PropertyType "string"]]]]

现在,作为示例,我想提取所有 PropertyType。我有两个主要方法,我想要一个解决方案。

  1. 通过指定路径;像 [:ClassDescription :Properties :Property :PropertyType]
  2. 通过提取所有 :PropertyType 个元素,不考虑深度。

对于 A.,我的第一个想法是通过 insta/transform 将它的某些部分转换为地图,然后使用 get-in,但后来我得到了一个非常笨拙的解决方案,涉及嵌套循环和 get-in.

我也可以用nth,把自己钻进结构里,不过好像很麻烦,再加一层就容易坏

我的另一个想法是递归解决方案,我以相同的方式处理每个元素并遍历它并检查所有匹配项。

对于 B,到目前为止,我唯一的解决方案是一个递归函数,它只钻取所有内容并尝试匹配第一个元素。

我相信这些 "handwritten" 函数可以通过 insta/transformmapfilterreduce 等的巧妙组合来避免。可以是吗?

要从解析的数据中获取元素,您可以使用 tree-seq 遍历所有元素并选择您需要的元素。例如:

(defn parsed-tree-seq [parsed]
  (tree-seq #(vector? (second %)) rest parsed))

(map second
     (filter
       #(= (first %) :PropertyType)
       (parsed-tree-seq parsed)))
; => ("Int" "string")

然而,您最好首先在解析器中使用 <...> 来塑造您的数据 and/or 通过将它们变成更具地图感的东西,这样更容易访问转型。例如。将 :Properties 变成地图列表并将整个事物变成地图:

(defn transform [parsed]
  (insta/transform
    {:Property #(into {} %&)
     :Properties #(vec (concat [:Properties] [%&]))
     :ClassDescription #(into {} %&)
     } parsed))

(map :PropertyType (:Properties (transform parsed)))
; => ("Int" "string")