如何从instaparse遍历解析树

时间:2015-04-10 08:02:32

标签: clojure instaparse

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

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

现在,作为一个例子,我想提取所有PropertyTypes。我有两种主要方式,我想为两者提供解决方案。

  1. 通过指定路径;像[:ClassDescription :Properties :Property :PropertyType]
  2. 之类的东西
  3. 通过全部提取 :PropertyType元素,无论深度如何。
  4. 对于A.,我的第一个想法是通过insta/transform将其某些部分转换为地图,然后使用get-in,但后来我得到了一个非常笨重的解决方案,涉及嵌套循环和{{1} }。

    我也可以使用get-in,并将自己钻进结构中,但这看起来很麻烦,如果我添加另一层,它会轻易破解。

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

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

    我相信这些“手写”功能可以通过nthinsta/transformmapfilter等的巧妙组合来避免。可以吗?

1 个答案:

答案 0 :(得分:4)

为了从解析的数据中获取元素,您可以使用tree-seq迭代所有元素并选择所需内容。 E.g:

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

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

然而,您可能最好通过在解析器中使用<...>和/或将它们转换为更加贴图的内容来更好地塑造数据,通过转换更容易访问。例如。将:Properties转换为地图列表并将整个内容转换为地图:

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

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