Как пройти парсинг дерева от инстапарсе

Я экспериментирую с Clojure и Instaparse. Я создал маленький игрушечный язык и застрял в том, как правильно обрабатывать получающееся дерево. Вот что я получаю:

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

Теперь, в качестве примера, я хотел бы извлечь все PropertyTypes. У меня есть два основных способа, и я хотел бы найти решение для обоих.

  1. Указав путь; что-то вроде [:ClassDescription :Properties :Property :PropertyType]
  2. Извлекая все:PropertyType элементы, независимо от глубины.

Для А. моей первой идеей было преобразовать некоторые его части в карты с помощью insta/transform а затем использовать get-in, но потом я получаю действительно неуклюжее решение, включающее вложенные циклы и get-in,

Я также могу использовать nthи углубиться в структуру, но это кажется громоздким и легко сломается, если я добавлю еще один слой.

Моей другой идеей было рекурсивное решение, где я обращаюсь с каждым элементом одинаково, перебираю его и проверяю все совпадения.

Для Б. мое единственное решение до сих пор - это рекурсивная функция, которая просто просматривает все и пытается найти соответствие первому элементу.

Я считаю, что этих "рукописных" функций можно избежать с помощью некоторой умной комбинации insta/transform, map, filter, reduceи т.д. Может ли это?

1 ответ

Решение

Для получения элементов из проанализированных данных, вы можете использовать tree-seq итерировать все и выбрать то, что вам нужно. Например:

(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")
Другие вопросы по тегам