Как пройти парсинг дерева от инстапарсе
Я экспериментирую с Clojure и Instaparse. Я создал маленький игрушечный язык и застрял в том, как правильно обрабатывать получающееся дерево. Вот что я получаю:
[:ClassDescription
[:ClassName "Test"]
[:Properties
[:Property
[:PropertyName "ID"]
[:PropertyType "Int"]]
[:Property
[:PropertyName "Name"]
[:PropertyType "string"]]]]
Теперь, в качестве примера, я хотел бы извлечь все PropertyTypes. У меня есть два основных способа, и я хотел бы найти решение для обоих.
- Указав путь; что-то вроде
[:ClassDescription :Properties :Property :PropertyType]
- Извлекая все
: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")